Skip to content

Commit

Permalink
make Grpc C# work on aarch64 linux (#25717)
Browse files Browse the repository at this point in the history
* build aarch64 version of protoc

* remove csharp artifact x86 build logic for unix systems

* build grpc_csharp_ext artifact for linux aarch64

* refactor platform detection

* add generated dllimports for arm64

* fix native library loading on arm64

* include arm64 artifacts in Grpc.Tools

* add Grpc.Tools codegen support for linux aarch64

* grpc.tools cleanup
  • Loading branch information
jtattermusch committed Mar 17, 2021
1 parent 9fc27fb commit 21c83cb
Show file tree
Hide file tree
Showing 13 changed files with 664 additions and 122 deletions.
4 changes: 4 additions & 0 deletions src/csharp/Grpc.Core/Grpc.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
<PackagePath>runtimes/linux-x64/native/libgrpc_csharp_ext.x64.so</PackagePath>
<Pack>true</Pack>
</Content>
<Content Include="..\nativelibs\csharp_ext_linux_aarch64\libgrpc_csharp_ext.so">
<PackagePath>runtimes/linux-arm64/native/libgrpc_csharp_ext.arm64.so</PackagePath>
<Pack>true</Pack>
</Content>
<Content Include="..\nativelibs\csharp_ext_windows_x64\grpc_csharp_ext.dll">
<PackagePath>runtimes/win-x64/native/grpc_csharp_ext.x64.dll</PackagePath>
<Pack>true</Pack>
Expand Down
129 changes: 129 additions & 0 deletions src/csharp/Grpc.Core/Internal/CommonPlatformDetection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#region Copyright notice and license

// Copyright 2021 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#endregion

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Grpc.Core.Internal
{
/// <summary>
/// This source file is shared by both Grpc.Core and Grpc.Tools to avoid duplication
/// of platform detection code.
/// </summary>
internal static class CommonPlatformDetection
{
public enum OSKind { Unknown, Windows, Linux, MacOSX };
public enum CpuArchitecture { Unknown, X86, X64, Arm64 };

public static OSKind GetOSKind()
{
#if NETSTANDARD || NETCORE
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return OSKind.Windows;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return OSKind.Linux;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return OSKind.MacOSX;
}
else
{
return OSKind.Unknown;
}
#else
var platform = Environment.OSVersion.Platform;
if (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows)
{
return OSKind.Windows;
}
else if (platform == PlatformID.Unix && GetUname() == "Darwin")
{
// PlatformID.MacOSX is never returned, commonly used trick is to identify Mac is by using uname.
return OSKind.MacOSX;
}
else if (platform == PlatformID.Unix)
{
// on legacy .NET Framework, our detection options are limited, so we treat
// all other unix systems as Linux.
return OSKind.Linux;
}
else
{
return OSKind.Unknown;
}
#endif
}

public static CpuArchitecture GetProcessArchitecture()
{
#if NETSTANDARD || NETCORE
switch (RuntimeInformation.ProcessArchitecture)
{
case Architecture.X86:
return CpuArchitecture.X86;
case Architecture.X64:
return CpuArchitecture.X64;
case Architecture.Arm64:
return CpuArchitecture.Arm64;
// We do not support other architectures,
// so we simply return "unrecognized".
default:
return CpuArchitecture.Unknown;
}
#else
// on legacy .NET Framework, RuntimeInformation is not available
// but our choice of supported architectures there
// is also very limited, so we simply take our best guess.
return Environment.Is64BitProcess ? CpuArchitecture.X64 : CpuArchitecture.X86;
#endif
}

[DllImport("libc")]
static extern int uname(IntPtr buf);

// This code is copied from Grpc.Core/PlatformApis.cs
static string GetUname()
{
var buffer = Marshal.AllocHGlobal(8192);
try
{
if (uname(buffer) == 0)
{
return Marshal.PtrToStringAnsi(buffer);
}
return string.Empty;
}
catch
{
return string.Empty;
}
finally
{
if (buffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(buffer);
}
}
}
}
}
28 changes: 19 additions & 9 deletions src/csharp/Grpc.Core/Internal/NativeExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,22 +136,30 @@ private static NativeMethods LoadNativeMethodsUsingDllImports()
// but DllImport("grpc_csharp_ext.x64.dll") does, so we need a special case for that.
// See https://github.com/dotnet/coreclr/pull/17505 (fixed in .NET Core 3.1+)
bool useDllSuffix = PlatformApis.IsWindows;
if (PlatformApis.Is64Bit)
if (PlatformApis.ProcessArchitecture == CommonPlatformDetection.CpuArchitecture.X64)
{
if (useDllSuffix)
{
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x64_dll());
}
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x64());
}
else
else if (PlatformApis.ProcessArchitecture == CommonPlatformDetection.CpuArchitecture.X86)
{
if (useDllSuffix)
{
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x86_dll());
}
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_x86());
}
else if (PlatformApis.ProcessArchitecture == CommonPlatformDetection.CpuArchitecture.Arm64)
{
return new NativeMethods(new NativeMethods.DllImportsFromSharedLib_arm64());
}
else
{
throw new InvalidOperationException($"Unsupported architecture \"{PlatformApis.ProcessArchitecture}\".");
}
}

/// <summary>
Expand Down Expand Up @@ -285,16 +293,18 @@ private static string GetRuntimeIdString()
throw new InvalidOperationException("Unsupported platform.");
}

// Currently, only Intel platform is supported.
private static string GetArchitectureString()
{
if (PlatformApis.Is64Bit)
{
return "x64";
}
else
switch (PlatformApis.ProcessArchitecture)
{
return "x86";
case CommonPlatformDetection.CpuArchitecture.X86:
return "x86";
case CommonPlatformDetection.CpuArchitecture.X64:
return "x64";
case CommonPlatformDetection.CpuArchitecture.Arm64:
return "arm64";
default:
throw new InvalidOperationException($"Unsupported architecture \"{PlatformApis.ProcessArchitecture}\".");
}
}

Expand Down

0 comments on commit 21c83cb

Please sign in to comment.