revamp C# native library loader
diff --git a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
index af55cb0..ea6572e 100644
--- a/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
+++ b/src/csharp/Grpc.Core.Tests/PInvokeTest.cs
@@ -45,14 +45,10 @@
 {
     public class PInvokeTest
     {
+        static readonly NativeMethods Native = NativeMethods.Get();
+
         int counter;
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_test_nop(IntPtr ptr);
-
         /// <summary>
         /// (~1.26us .NET Windows)
         /// </summary>
@@ -87,7 +83,7 @@
                 1000000, 10000000,
                 () =>
                 {
-                    grpcsharp_test_callback(handler);
+                    Native.grpcsharp_test_callback(handler);
                 });
             Assert.AreNotEqual(0, counter);
         }
@@ -106,7 +102,7 @@
                 10000, 10000,
                 () =>
                 {
-                    grpcsharp_test_callback(new OpCompletionDelegate(Handler));
+                    Native.grpcsharp_test_callback(new OpCompletionDelegate(Handler));
                 });
             Assert.AreNotEqual(0, counter);
         }
@@ -122,7 +118,7 @@
                 1000000, 100000000,
                 () =>
                 {
-                    grpcsharp_test_nop(IntPtr.Zero);
+                    Native.grpcsharp_test_nop(IntPtr.Zero);
                 });
         }
 
diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj
index 852b212..ef4ec70 100644
--- a/src/csharp/Grpc.Core/Grpc.Core.csproj
+++ b/src/csharp/Grpc.Core/Grpc.Core.csproj
@@ -19,7 +19,6 @@
     <DefineConstants>DEBUG;</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <NativeDependenciesConfiguration>Debug</NativeDependenciesConfiguration>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -27,7 +26,6 @@
     <OutputPath>bin\Release</OutputPath>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
-    <NativeDependenciesConfiguration>Release</NativeDependenciesConfiguration>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseSigned|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -38,7 +36,6 @@
     <WarningLevel>4</WarningLevel>
     <SignAssembly>True</SignAssembly>
     <AssemblyOriginatorKeyFile>C:\keys\Grpc.snk</AssemblyOriginatorKeyFile>
-    <NativeDependenciesConfiguration>Release</NativeDependenciesConfiguration>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
@@ -52,6 +49,10 @@
     <Compile Include="AsyncAuthInterceptor.cs" />
     <Compile Include="CallCredentials.cs" />
     <Compile Include="IClientStreamWriter.cs" />
+    <Compile Include="Internal\NativeMethods.cs" />
+    <Compile Include="Internal\PlatformApis.cs" />
+    <Compile Include="Internal\NativeExtension.cs" />
+    <Compile Include="Internal\UnmanagedLibrary.cs" />
     <Compile Include="Internal\NativeMetadataCredentialsPlugin.cs" />
     <Compile Include="Internal\INativeCall.cs" />
     <Compile Include="IServerStreamWriter.cs" />
@@ -131,20 +132,6 @@
     <None Include="Grpc.Core.nuspec" />
     <None Include="packages.config" />
   </ItemGroup>
-  <Choose>
-    <!-- Under older versions of Monodevelop, Choose is not supported and is just
-         ignored, which gives us the desired effect. -->
-    <When Condition=" '$(OS)' != 'Unix' ">
-      <ItemGroup>
-        <Content Include="..\..\..\vsprojects\$(NativeDependenciesConfiguration)\grpc_csharp_ext.dll">
-          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-        </Content>
-      </ItemGroup>
-    </When>
-    <Otherwise />
-  </Choose>
+  <Import Project="NativeDeps.targets" />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-  <ItemGroup />
-  <ItemGroup />
-  <ItemGroup />
 </Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/GrpcEnvironment.cs b/src/csharp/Grpc.Core/GrpcEnvironment.cs
index e7c0418..3797100 100644
--- a/src/csharp/Grpc.Core/GrpcEnvironment.cs
+++ b/src/csharp/Grpc.Core/GrpcEnvironment.cs
@@ -47,15 +47,6 @@
     {
         const int THREAD_POOL_SIZE = 4;
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_init();
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_shutdown();
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_version_string();  // returns not-owned const char*
-
         static object staticLock = new object();
         static GrpcEnvironment instance;
         static int refCount;
@@ -136,7 +127,6 @@
         /// </summary>
         private GrpcEnvironment()
         {
-            NativeLogRedirector.Redirect();
             GrpcNativeInit();
             completionRegistry = new CompletionRegistry(this);
             threadPool = new GrpcThreadPool(this, THREAD_POOL_SIZE);
@@ -181,18 +171,18 @@
         /// </summary>
         internal static string GetCoreVersionString()
         {
-            var ptr = grpcsharp_version_string();  // the pointer is not owned
+            var ptr = NativeMethods.Get().grpcsharp_version_string();  // the pointer is not owned
             return Marshal.PtrToStringAnsi(ptr);
         }
 
         internal static void GrpcNativeInit()
         {
-            grpcsharp_init();
+            NativeMethods.Get().grpcsharp_init();
         }
 
         internal static void GrpcNativeShutdown()
         {
-            grpcsharp_shutdown();
+            NativeMethods.Get().grpcsharp_shutdown();
         }
 
         /// <summary>
diff --git a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
index 3a96414..256d2e3 100644
--- a/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/BatchContextSafeHandle.cs
@@ -42,47 +42,7 @@
     /// </summary>
     internal class BatchContextSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern BatchContextSafeHandle grpcsharp_batch_context_create();
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx);  // returns const char*
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CallSafeHandle grpcsharp_batch_context_server_rpc_new_call(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_server_rpc_new_method(BatchContextSafeHandle ctx);  // returns const char*
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_server_rpc_new_host(BatchContextSafeHandle ctx);  // returns const char*
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern Timespec grpcsharp_batch_context_server_rpc_new_deadline(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_batch_context_server_rpc_new_request_metadata(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_batch_context_destroy(IntPtr ctx);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private BatchContextSafeHandle()
         {
@@ -90,7 +50,7 @@
 
         public static BatchContextSafeHandle Create()
         {
-            return grpcsharp_batch_context_create();
+            return Native.grpcsharp_batch_context_create();
         }
 
         public IntPtr Handle
@@ -104,17 +64,17 @@
         // Gets data of recv_initial_metadata completion.
         public Metadata GetReceivedInitialMetadata()
         {
-            IntPtr metadataArrayPtr = grpcsharp_batch_context_recv_initial_metadata(this);
+            IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_initial_metadata(this);
             return MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr);
         }
             
         // Gets data of recv_status_on_client completion.
         public ClientSideStatus GetReceivedStatusOnClient()
         {
-            string details = Marshal.PtrToStringAnsi(grpcsharp_batch_context_recv_status_on_client_details(this));
-            var status = new Status(grpcsharp_batch_context_recv_status_on_client_status(this), details);
+            string details = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_recv_status_on_client_details(this));
+            var status = new Status(Native.grpcsharp_batch_context_recv_status_on_client_status(this), details);
 
-            IntPtr metadataArrayPtr = grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this);
+            IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_recv_status_on_client_trailing_metadata(this);
             var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr);
 
             return new ClientSideStatus(status, metadata);
@@ -123,26 +83,26 @@
         // Gets data of recv_message completion.
         public byte[] GetReceivedMessage()
         {
-            IntPtr len = grpcsharp_batch_context_recv_message_length(this);
+            IntPtr len = Native.grpcsharp_batch_context_recv_message_length(this);
             if (len == new IntPtr(-1))
             {
                 return null;
             }
             byte[] data = new byte[(int)len];
-            grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length));
+            Native.grpcsharp_batch_context_recv_message_to_buffer(this, data, new UIntPtr((ulong)data.Length));
             return data;
         }
 
         // Gets data of server_rpc_new completion.
         public ServerRpcNew GetServerRpcNew(Server server)
         {
-            var call = grpcsharp_batch_context_server_rpc_new_call(this);
+            var call = Native.grpcsharp_batch_context_server_rpc_new_call(this);
 
-            var method = Marshal.PtrToStringAnsi(grpcsharp_batch_context_server_rpc_new_method(this));
-            var host = Marshal.PtrToStringAnsi(grpcsharp_batch_context_server_rpc_new_host(this));
-            var deadline = grpcsharp_batch_context_server_rpc_new_deadline(this);
+            var method = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_server_rpc_new_method(this));
+            var host = Marshal.PtrToStringAnsi(Native.grpcsharp_batch_context_server_rpc_new_host(this));
+            var deadline = Native.grpcsharp_batch_context_server_rpc_new_deadline(this);
 
-            IntPtr metadataArrayPtr = grpcsharp_batch_context_server_rpc_new_request_metadata(this);
+            IntPtr metadataArrayPtr = Native.grpcsharp_batch_context_server_rpc_new_request_metadata(this);
             var metadata = MetadataArraySafeHandle.ReadMetadataFromPtrUnsafe(metadataArrayPtr);
 
             return new ServerRpcNew(server, call, method, host, deadline, metadata);
@@ -151,12 +111,12 @@
         // Gets data of receive_close_on_server completion.
         public bool GetReceivedCloseOnServerCancelled()
         {
-            return grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0;
+            return Native.grpcsharp_batch_context_recv_close_on_server_cancelled(this) != 0;
         }
             
         protected override bool ReleaseHandle()
         {
-            grpcsharp_batch_context_destroy(handle);
+            Native.grpcsharp_batch_context_destroy(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs
index 92fbe8c..0221798 100644
--- a/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CStringSafeHandle.cs
@@ -39,8 +39,7 @@
     /// </summary>
     internal class CStringSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void gprsharp_free(IntPtr ptr);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private CStringSafeHandle()
         {
@@ -53,7 +52,7 @@
 
         protected override bool ReleaseHandle()
         {
-            gprsharp_free(handle);
+            Native.gprsharp_free(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/CallCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallCredentialsSafeHandle.cs
index 3678c7d..3095a34 100644
--- a/src/csharp/Grpc.Core/Internal/CallCredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallCredentialsSafeHandle.cs
@@ -40,11 +40,7 @@
     /// </summary>
     internal class CallCredentialsSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_call_credentials_release(IntPtr credentials);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private CallCredentialsSafeHandle()
         {
@@ -52,12 +48,12 @@
 
         public static CallCredentialsSafeHandle CreateComposite(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2)
         {
-            return grpcsharp_composite_call_credentials_create(creds1, creds2);
+            return Native.grpcsharp_composite_call_credentials_create(creds1, creds2);
         }
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_call_credentials_release(handle);
+            Native.grpcsharp_call_credentials_release(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
index 69dbdfe..454f88d 100644
--- a/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CallSafeHandle.cs
@@ -44,71 +44,12 @@
     internal class CallSafeHandle : SafeHandleZeroIsInvalid, INativeCall
     {
         public static readonly CallSafeHandle NullInstance = new CallSafeHandle();
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         const uint GRPC_WRITE_BUFFER_HINT = 1;
         CompletionRegistry completionRegistry;
         CompletionQueueSafeHandle completionQueue;
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_cancel(CallSafeHandle call);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call,
-            BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call,
-            BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call,
-            BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len,
-            MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call,
-            BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call,
-            BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call,
-            BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call, 
-            BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call,
-            BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call,
-            BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
-            BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call,
-            BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_call_set_credentials(CallSafeHandle call, CallCredentialsSafeHandle credentials);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_call_destroy(IntPtr call);
-
         private CallSafeHandle()
         {
         }
@@ -121,7 +62,7 @@
 
         public void SetCredentials(CallCredentialsSafeHandle credentials)
         {
-            grpcsharp_call_set_credentials(this, credentials).CheckOk();
+            Native.grpcsharp_call_set_credentials(this, credentials).CheckOk();
         }
 
         public void StartUnary(UnaryResponseClientHandler callback, byte[] payload, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags)
@@ -130,7 +71,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
-                grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
+                Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
                     .CheckOk();
             }
         }
@@ -139,7 +80,7 @@
         {
             using (Profilers.ForCurrentThread().NewScope("CallSafeHandle.StartUnary"))
             {
-                grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
+                Native.grpcsharp_call_start_unary(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags)
                     .CheckOk();
             }
         }
@@ -150,7 +91,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient(), context.GetReceivedMessage(), context.GetReceivedInitialMetadata()));
-                grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
+                Native.grpcsharp_call_start_client_streaming(this, ctx, metadataArray).CheckOk();
             }
         }
 
@@ -160,7 +101,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
-                grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk();
+                Native.grpcsharp_call_start_server_streaming(this, ctx, payload, new UIntPtr((ulong)payload.Length), metadataArray, writeFlags).CheckOk();
             }
         }
 
@@ -170,7 +111,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedStatusOnClient()));
-                grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
+                Native.grpcsharp_call_start_duplex_streaming(this, ctx, metadataArray).CheckOk();
             }
         }
 
@@ -180,7 +121,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
-                grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk();
+                Native.grpcsharp_call_send_message(this, ctx, payload, new UIntPtr((ulong)payload.Length), writeFlags, sendEmptyInitialMetadata).CheckOk();
             }
         }
 
@@ -190,7 +131,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
-                grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
+                Native.grpcsharp_call_send_close_from_client(this, ctx).CheckOk();
             }
         }
 
@@ -200,7 +141,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
-                grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk();
+                Native.grpcsharp_call_send_status_from_server(this, ctx, status.StatusCode, status.Detail, metadataArray, sendEmptyInitialMetadata).CheckOk();
             }
         }
 
@@ -210,7 +151,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedMessage()));
-                grpcsharp_call_recv_message(this, ctx).CheckOk();
+                Native.grpcsharp_call_recv_message(this, ctx).CheckOk();
             }
         }
 
@@ -220,7 +161,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedInitialMetadata()));
-                grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk();
+                Native.grpcsharp_call_recv_initial_metadata(this, ctx).CheckOk();
             }
         }
 
@@ -230,7 +171,7 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success, context.GetReceivedCloseOnServerCancelled()));
-                grpcsharp_call_start_serverside(this, ctx).CheckOk();
+                Native.grpcsharp_call_start_serverside(this, ctx).CheckOk();
             }
         }
 
@@ -240,23 +181,23 @@
             {
                 var ctx = BatchContextSafeHandle.Create();
                 completionRegistry.RegisterBatchCompletion(ctx, (success, context) => callback(success));
-                grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk();
+                Native.grpcsharp_call_send_initial_metadata(this, ctx, metadataArray).CheckOk();
             }
         }
 
         public void Cancel()
         {
-            grpcsharp_call_cancel(this).CheckOk();
+            Native.grpcsharp_call_cancel(this).CheckOk();
         }
 
         public void CancelWithStatus(Status status)
         {
-            grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk();
+            Native.grpcsharp_call_cancel_with_status(this, status.StatusCode, status.Detail).CheckOk();
         }
 
         public string GetPeer()
         {
-            using (var cstring = grpcsharp_call_get_peer(this))
+            using (var cstring = Native.grpcsharp_call_get_peer(this))
             {
                 return cstring.GetValue();
             }
@@ -264,7 +205,7 @@
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_call_destroy(handle);
+            Native.grpcsharp_call_destroy(handle);
             return true;
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
index ea5b523..0038024 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelArgsSafeHandle.cs
@@ -39,17 +39,7 @@
     /// </summary>
     internal class ChannelArgsSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs);
-
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
-
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_channel_args_destroy(IntPtr args);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private ChannelArgsSafeHandle()
         {
@@ -62,22 +52,22 @@
 
         public static ChannelArgsSafeHandle Create(int size)
         {
-            return grpcsharp_channel_args_create(new UIntPtr((uint)size));
+            return Native.grpcsharp_channel_args_create(new UIntPtr((uint)size));
         }
 
         public void SetString(int index, string key, string value)
         {
-            grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value);
+            Native.grpcsharp_channel_args_set_string(this, new UIntPtr((uint)index), key, value);
         }
 
         public void SetInteger(int index, string key, int value)
         {
-            grpcsharp_channel_args_set_integer(this, new UIntPtr((uint)index), key, value);
+            Native.grpcsharp_channel_args_set_integer(this, new UIntPtr((uint)index), key, value);
         }
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_channel_args_destroy(handle);
+            Native.grpcsharp_channel_args_destroy(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs
index 8a58c64..c85f552 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelCredentialsSafeHandle.cs
@@ -40,14 +40,7 @@
     /// </summary>
     internal class ChannelCredentialsSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_channel_credentials_release(IntPtr credentials);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private ChannelCredentialsSafeHandle()
         {
@@ -64,22 +57,22 @@
         {
             if (keyCertPair != null)
             {
-                return grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey);
+                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, keyCertPair.CertificateChain, keyCertPair.PrivateKey);
             }
             else
             {
-                return grpcsharp_ssl_credentials_create(pemRootCerts, null, null);
+                return Native.grpcsharp_ssl_credentials_create(pemRootCerts, null, null);
             }
         }
 
         public static ChannelCredentialsSafeHandle CreateComposite(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds)
         {
-            return grpcsharp_composite_channel_credentials_create(channelCreds, callCreds);
+            return Native.grpcsharp_composite_channel_credentials_create(channelCreds, callCreds);
         }
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_channel_credentials_release(handle);
+            Native.grpcsharp_channel_credentials_release(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
index 4a55841..1dbd1f4 100644
--- a/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ChannelSafeHandle.cs
@@ -41,27 +41,7 @@
     /// </summary>
     internal class ChannelSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelSafeHandle grpcsharp_secure_channel_create(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState,
-            Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_channel_destroy(IntPtr channel);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private ChannelSafeHandle()
         {
@@ -72,7 +52,7 @@
             // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle.
             // Doing so would make object finalizer crash if we end up abandoning the handle.
             GrpcEnvironment.GrpcNativeInit();
-            return grpcsharp_insecure_channel_create(target, channelArgs);
+            return Native.grpcsharp_insecure_channel_create(target, channelArgs);
         }
 
         public static ChannelSafeHandle CreateSecure(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs)
@@ -80,14 +60,14 @@
             // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle.
             // Doing so would make object finalizer crash if we end up abandoning the handle.
             GrpcEnvironment.GrpcNativeInit();
-            return grpcsharp_secure_channel_create(credentials, target, channelArgs);
+            return Native.grpcsharp_secure_channel_create(credentials, target, channelArgs);
         }
 
         public CallSafeHandle CreateCall(CompletionRegistry registry, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline, CallCredentialsSafeHandle credentials)
         {
             using (Profilers.ForCurrentThread().NewScope("ChannelSafeHandle.CreateCall"))
             {
-                var result = grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
+                var result = Native.grpcsharp_channel_create_call(this, parentCall, propagationMask, cq, method, host, deadline);
                 if (credentials != null)
                 {
                     result.SetCredentials(credentials);
@@ -99,7 +79,7 @@
 
         public ChannelState CheckConnectivityState(bool tryToConnect)
         {
-            return grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0);
+            return Native.grpcsharp_channel_check_connectivity_state(this, tryToConnect ? 1 : 0);
         }
 
         public void WatchConnectivityState(ChannelState lastObservedState, Timespec deadline, CompletionQueueSafeHandle cq,
@@ -107,12 +87,12 @@
         {
             var ctx = BatchContextSafeHandle.Create();
             completionRegistry.RegisterBatchCompletion(ctx, callback);
-            grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx);
+            Native.grpcsharp_channel_watch_connectivity_state(this, lastObservedState, deadline, cq, ctx);
         }
 
         public string GetTarget()
         {
-            using (var cstring = grpcsharp_channel_get_target(this))
+            using (var cstring = Native.grpcsharp_channel_get_target(this))
             {
                 return cstring.GetValue();
             }
@@ -120,7 +100,7 @@
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_channel_destroy(handle);
+            Native.grpcsharp_channel_destroy(handle);
             GrpcEnvironment.GrpcNativeShutdown();
             return true;
         }
diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs
index 3f51751..2886807 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionQueueEvent.cs
@@ -42,8 +42,7 @@
     [StructLayout(LayoutKind.Sequential)]
     internal struct CompletionQueueEvent
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern int grpcsharp_sizeof_grpc_event();
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         public GRPCCompletionType type;
         public int success;
@@ -53,7 +52,7 @@
         {
             get
             {
-                return grpcsharp_sizeof_grpc_event();
+                return Native.grpcsharp_sizeof_grpc_event();
             }
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
index 3754ad3..365de96 100644
--- a/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/CompletionQueueSafeHandle.cs
@@ -42,43 +42,30 @@
     /// </summary>
     internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid
     {
+        static readonly NativeMethods Native = NativeMethods.Get();
+
         AtomicCounter shutdownRefcount = new AtomicCounter(1);
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create();
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_completion_queue_destroy(IntPtr cq);
-
         private CompletionQueueSafeHandle()
         {
         }
 
         public static CompletionQueueSafeHandle Create()
         {
-            return grpcsharp_completion_queue_create();
+            return Native.grpcsharp_completion_queue_create();
 
         }
 
         public CompletionQueueEvent Next()
         {
-            return grpcsharp_completion_queue_next(this);
+            return Native.grpcsharp_completion_queue_next(this);
         }
 
         public CompletionQueueEvent Pluck(IntPtr tag)
         {
             using (Profilers.ForCurrentThread().NewScope("CompletionQueueSafeHandle.Pluck"))
             {
-                return grpcsharp_completion_queue_pluck(this, tag);
+                return Native.grpcsharp_completion_queue_pluck(this, tag);
             }
         }
 
@@ -98,7 +85,7 @@
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_completion_queue_destroy(handle);
+            Native.grpcsharp_completion_queue_destroy(handle);
             return true;
         }
 
@@ -106,7 +93,7 @@
         {
             if (shutdownRefcount.Decrement() == 0)
             {
-                grpcsharp_completion_queue_shutdown(this);
+                Native.grpcsharp_completion_queue_shutdown(this);
             }
         }
 
diff --git a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
index ed1bd24..25735d5 100644
--- a/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/MetadataArraySafeHandle.cs
@@ -40,26 +40,7 @@
     /// </summary>
     internal class MetadataArraySafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity);
-
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern UIntPtr grpcsharp_metadata_array_count(IntPtr metadataArray);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_metadata_array_get_key(IntPtr metadataArray, UIntPtr index);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern UIntPtr grpcsharp_metadata_array_get_value_length(IntPtr metadataArray, UIntPtr index);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_metadata_array_destroy_full(IntPtr array);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private MetadataArraySafeHandle()
         {
@@ -70,11 +51,11 @@
             using (Profilers.ForCurrentThread().NewScope("MetadataArraySafeHandle.Create"))
             {
                 // TODO(jtattermusch): we might wanna check that the metadata is readonly 
-                var metadataArray = grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
+                var metadataArray = Native.grpcsharp_metadata_array_create(new UIntPtr((ulong)metadata.Count));
                 for (int i = 0; i < metadata.Count; i++)
                 {
                     var valueBytes = metadata[i].GetSerializedValueUnsafe();
-                    grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length));
+                    Native.grpcsharp_metadata_array_add(metadataArray, metadata[i].Key, valueBytes, new UIntPtr((ulong)valueBytes.Length));
                 }
                 return metadataArray;
             }
@@ -90,15 +71,15 @@
                 return null;
             }
 
-            ulong count = grpcsharp_metadata_array_count(metadataArray).ToUInt64();
+            ulong count = Native.grpcsharp_metadata_array_count(metadataArray).ToUInt64();
 
             var metadata = new Metadata();
             for (ulong i = 0; i < count; i++)
             {
                 var index = new UIntPtr(i);
-                string key = Marshal.PtrToStringAnsi(grpcsharp_metadata_array_get_key(metadataArray, index));
-                var bytes = new byte[grpcsharp_metadata_array_get_value_length(metadataArray, index).ToUInt64()];
-                Marshal.Copy(grpcsharp_metadata_array_get_value(metadataArray, index), bytes, 0, bytes.Length);
+                string key = Marshal.PtrToStringAnsi(Native.grpcsharp_metadata_array_get_key(metadataArray, index));
+                var bytes = new byte[Native.grpcsharp_metadata_array_get_value_length(metadataArray, index).ToUInt64()];
+                Marshal.Copy(Native.grpcsharp_metadata_array_get_value(metadataArray, index), bytes, 0, bytes.Length);
                 metadata.Add(Metadata.Entry.CreateUnsafe(key, bytes));
             }
             return metadata;
@@ -114,7 +95,7 @@
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_metadata_array_destroy_full(handle);
+            Native.grpcsharp_metadata_array_destroy_full(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/NativeExtension.cs b/src/csharp/Grpc.Core/Internal/NativeExtension.cs
new file mode 100644
index 0000000..3a4ebf9
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/NativeExtension.cs
@@ -0,0 +1,158 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+
+using Grpc.Core.Logging;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Takes care of loading C# native extension and provides access to PInvoke calls the library exports.
+    /// </summary>
+    internal sealed class NativeExtension
+    {
+        const string NativeLibrariesDir = "nativelibs";
+
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeExtension>();
+        static readonly object staticLock = new object();
+        static volatile NativeExtension instance;
+
+        readonly NativeMethods nativeMethods;
+
+        private NativeExtension()
+        {
+            this.nativeMethods = new NativeMethods(Load());
+            
+            // Redirect the the native logs as the very first thing after loading the native extension
+            // to make sure we don't lose any logs.
+            NativeLogRedirector.Redirect(this.nativeMethods);
+
+            Logger.Debug("gRPC native library loaded successfully.");
+        }
+
+        /// <summary>
+        /// Gets singleton instance of this class.
+        /// The native extension is loaded when called for the first time.
+        /// </summary>
+        public static NativeExtension Get()
+        {
+            if (instance == null)
+            {
+                lock (staticLock)
+                {
+                    if (instance == null) {
+                        instance = new NativeExtension();
+                    }
+                }
+            }
+            return instance;
+        }
+
+        /// <summary>
+        /// Provides access to the exported native methods.
+        /// </summary>
+        public NativeMethods NativeMethods
+        {
+            get { return this.nativeMethods; }
+        }
+
+        /// <summary>
+        /// Detects which configuration of native extension to load and load it.
+        /// </summary>
+        private static UnmanagedLibrary Load()
+        {
+            // TODO: allow customizing path to native extension (possibly through exposing a GrpcEnvironment property).
+
+            var libraryFlavor = string.Format("{0}_{1}", GetPlatformString(), GetArchitectureString());
+            var fullPath = Path.Combine(GetExecutingAssemblyDirectory(),
+                NativeLibrariesDir, libraryFlavor, GetNativeLibraryFilename());
+            return new UnmanagedLibrary(fullPath);
+        }
+
+        private static string GetExecutingAssemblyDirectory()
+        {
+            return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+        }
+
+        private static string GetPlatformString()
+        {
+            if (PlatformApis.IsWindows)
+            {
+                return "windows";
+            }
+            if (PlatformApis.IsLinux)
+            {
+                return "linux";
+            }
+            if (PlatformApis.IsMacOSX)
+            {
+                return "macosx";
+            }
+            throw new InvalidOperationException("Unsupported platform.");
+        }
+
+        // Currently, only Intel platform is supported.
+        private static string GetArchitectureString()
+        {
+            if (PlatformApis.Is64Bit)
+            {
+                return "x64";
+            }
+            else
+            {
+                return "x86";
+            }
+        }
+
+        // platform specific file name of the extension library
+        private static string GetNativeLibraryFilename()
+        {
+            if (PlatformApis.IsWindows)
+            {
+                return "grpc_csharp_ext.dll";
+            }
+            if (PlatformApis.IsLinux)
+            {
+                return "libgrpc_csharp_ext.so";
+            }
+            if (PlatformApis.IsMacOSX)
+            {
+                return "libgrpc_csharp_ext.dylib";
+            }
+            throw new InvalidOperationException("Unsupported platform.");
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
index b8a55c5..3fcf867 100644
--- a/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeLogRedirector.cs
@@ -51,20 +51,17 @@
         static object staticLock = new object();
         static GprLogDelegate writeCallback;
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_redirect_log(GprLogDelegate callback);
-
         /// <summary>
         /// Redirects logs from native gRPC C core library to a general logger.
         /// </summary>
-        public static void Redirect()
+        public static void Redirect(NativeMethods native)
         {
             lock (staticLock)
             {
                 if (writeCallback == null)
                 {
                     writeCallback = new GprLogDelegate(HandleWrite);
-                    grpcsharp_redirect_log(writeCallback);    
+                    native.grpcsharp_redirect_log(writeCallback);
                 }
             }
         }
diff --git a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
index 8bb646d..ab9b8e9 100644
--- a/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
+++ b/src/csharp/Grpc.Core/Internal/NativeMetadataCredentialsPlugin.cs
@@ -44,12 +44,7 @@
     {
         const string GetMetadataExceptionMsg = "Exception occured in metadata credentials plugin.";
         static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<NativeMetadataCredentialsPlugin>();
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor);
-        
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         AsyncAuthInterceptor interceptor;
         GCHandle gcHandle;
@@ -63,7 +58,7 @@
 
             // Make sure the callback doesn't get garbage collected until it is destroyed.
             this.gcHandle = GCHandle.Alloc(this.nativeInterceptor, GCHandleType.Normal);
-            this.credentials = grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor);
+            this.credentials = Native.grpcsharp_metadata_credentials_create_from_plugin(nativeInterceptor);
         }
 
         public CallCredentialsSafeHandle Credentials
@@ -87,7 +82,7 @@
             }
             catch (Exception e)
             {
-                grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg);
+                Native.grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg);
                 Logger.Error(e, GetMetadataExceptionMsg);
             }
         }
@@ -101,12 +96,12 @@
 
                 using (var metadataArray = MetadataArraySafeHandle.Create(metadata))
                 {
-                    grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null);
+                    Native.grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, metadataArray, StatusCode.OK, null);
                 }
             }
             catch (Exception e)
             {
-                grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg);
+                Native.grpcsharp_metadata_credentials_notify_from_plugin(callbackPtr, userDataPtr, MetadataArraySafeHandle.Create(Metadata.Empty), StatusCode.Unknown, GetMetadataExceptionMsg);
                 Logger.Error(e, GetMetadataExceptionMsg);
             }
         }
diff --git a/src/csharp/Grpc.Core/Internal/NativeMethods.cs b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
new file mode 100644
index 0000000..b79d7c7
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/NativeMethods.cs
@@ -0,0 +1,816 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using Grpc.Core.Logging;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Provides access to all native methods provided by <c>NativeExtension</c>.
+    /// An extra level of indirection is added to P/Invoke calls to allow intelligent loading
+    /// of the right configuration of the native extension based on current platform, architecture etc.
+    /// </summary>
+    internal class NativeMethods
+    {
+        #region Native methods
+
+        public readonly Delegates.grpcsharp_init_delegate grpcsharp_init;
+        public readonly Delegates.grpcsharp_shutdown_delegate grpcsharp_shutdown;
+        public readonly Delegates.grpcsharp_version_string_delegate grpcsharp_version_string;
+
+        public readonly Delegates.grpcsharp_batch_context_create_delegate grpcsharp_batch_context_create;
+        public readonly Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate grpcsharp_batch_context_recv_initial_metadata;
+        public readonly Delegates.grpcsharp_batch_context_recv_message_length_delegate grpcsharp_batch_context_recv_message_length;
+        public readonly Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate grpcsharp_batch_context_recv_message_to_buffer;
+        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate grpcsharp_batch_context_recv_status_on_client_status;
+        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate grpcsharp_batch_context_recv_status_on_client_details;
+        public readonly Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
+        public readonly Delegates.grpcsharp_batch_context_server_rpc_new_call_delegate grpcsharp_batch_context_server_rpc_new_call;
+        public readonly Delegates.grpcsharp_batch_context_server_rpc_new_method_delegate grpcsharp_batch_context_server_rpc_new_method;
+        public readonly Delegates.grpcsharp_batch_context_server_rpc_new_host_delegate grpcsharp_batch_context_server_rpc_new_host;
+        public readonly Delegates.grpcsharp_batch_context_server_rpc_new_deadline_delegate grpcsharp_batch_context_server_rpc_new_deadline;
+        public readonly Delegates.grpcsharp_batch_context_server_rpc_new_request_metadata_delegate grpcsharp_batch_context_server_rpc_new_request_metadata;
+        public readonly Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate grpcsharp_batch_context_recv_close_on_server_cancelled;
+        public readonly Delegates.grpcsharp_batch_context_destroy_delegate grpcsharp_batch_context_destroy;
+
+        public readonly Delegates.grpcsharp_composite_call_credentials_create_delegate grpcsharp_composite_call_credentials_create;
+        public readonly Delegates.grpcsharp_call_credentials_release_delegate grpcsharp_call_credentials_release;
+
+        public readonly Delegates.grpcsharp_call_cancel_delegate grpcsharp_call_cancel;
+        public readonly Delegates.grpcsharp_call_cancel_with_status_delegate grpcsharp_call_cancel_with_status;
+        public readonly Delegates.grpcsharp_call_start_unary_delegate grpcsharp_call_start_unary;
+        public readonly Delegates.grpcsharp_call_start_client_streaming_delegate grpcsharp_call_start_client_streaming;
+        public readonly Delegates.grpcsharp_call_start_server_streaming_delegate grpcsharp_call_start_server_streaming;
+        public readonly Delegates.grpcsharp_call_start_duplex_streaming_delegate grpcsharp_call_start_duplex_streaming;
+        public readonly Delegates.grpcsharp_call_send_message_delegate grpcsharp_call_send_message;
+        public readonly Delegates.grpcsharp_call_send_close_from_client_delegate grpcsharp_call_send_close_from_client;
+        public readonly Delegates.grpcsharp_call_send_status_from_server_delegate grpcsharp_call_send_status_from_server;
+        public readonly Delegates.grpcsharp_call_recv_message_delegate grpcsharp_call_recv_message;
+        public readonly Delegates.grpcsharp_call_recv_initial_metadata_delegate grpcsharp_call_recv_initial_metadata;
+        public readonly Delegates.grpcsharp_call_start_serverside_delegate grpcsharp_call_start_serverside;
+        public readonly Delegates.grpcsharp_call_send_initial_metadata_delegate grpcsharp_call_send_initial_metadata;
+        public readonly Delegates.grpcsharp_call_set_credentials_delegate grpcsharp_call_set_credentials;
+        public readonly Delegates.grpcsharp_call_get_peer_delegate grpcsharp_call_get_peer;
+        public readonly Delegates.grpcsharp_call_destroy_delegate grpcsharp_call_destroy;
+
+        public readonly Delegates.grpcsharp_channel_args_create_delegate grpcsharp_channel_args_create;
+        public readonly Delegates.grpcsharp_channel_args_set_string_delegate grpcsharp_channel_args_set_string;
+        public readonly Delegates.grpcsharp_channel_args_set_integer_delegate grpcsharp_channel_args_set_integer;
+        public readonly Delegates.grpcsharp_channel_args_destroy_delegate grpcsharp_channel_args_destroy;
+
+        public readonly Delegates.grpcsharp_ssl_credentials_create_delegate grpcsharp_ssl_credentials_create;
+        public readonly Delegates.grpcsharp_composite_channel_credentials_create_delegate grpcsharp_composite_channel_credentials_create;
+        public readonly Delegates.grpcsharp_channel_credentials_release_delegate grpcsharp_channel_credentials_release;
+
+        public readonly Delegates.grpcsharp_insecure_channel_create_delegate grpcsharp_insecure_channel_create;
+        public readonly Delegates.grpcsharp_secure_channel_create_delegate grpcsharp_secure_channel_create;
+        public readonly Delegates.grpcsharp_channel_create_call_delegate grpcsharp_channel_create_call;
+        public readonly Delegates.grpcsharp_channel_check_connectivity_state_delegate grpcsharp_channel_check_connectivity_state;
+        public readonly Delegates.grpcsharp_channel_watch_connectivity_state_delegate grpcsharp_channel_watch_connectivity_state;
+        public readonly Delegates.grpcsharp_channel_get_target_delegate grpcsharp_channel_get_target;
+        public readonly Delegates.grpcsharp_channel_destroy_delegate grpcsharp_channel_destroy;
+
+        public readonly Delegates.grpcsharp_sizeof_grpc_event_delegate grpcsharp_sizeof_grpc_event;
+
+        public readonly Delegates.grpcsharp_completion_queue_create_delegate grpcsharp_completion_queue_create;
+        public readonly Delegates.grpcsharp_completion_queue_shutdown_delegate grpcsharp_completion_queue_shutdown;
+        public readonly Delegates.grpcsharp_completion_queue_next_delegate grpcsharp_completion_queue_next;
+        public readonly Delegates.grpcsharp_completion_queue_pluck_delegate grpcsharp_completion_queue_pluck;
+        public readonly Delegates.grpcsharp_completion_queue_destroy_delegate grpcsharp_completion_queue_destroy;
+
+        public readonly Delegates.gprsharp_free_delegate gprsharp_free;
+
+        public readonly Delegates.grpcsharp_metadata_array_create_delegate grpcsharp_metadata_array_create;
+        public readonly Delegates.grpcsharp_metadata_array_add_delegate grpcsharp_metadata_array_add;
+        public readonly Delegates.grpcsharp_metadata_array_count_delegate grpcsharp_metadata_array_count;
+        public readonly Delegates.grpcsharp_metadata_array_get_key_delegate grpcsharp_metadata_array_get_key;
+        public readonly Delegates.grpcsharp_metadata_array_get_value_delegate grpcsharp_metadata_array_get_value;
+        public readonly Delegates.grpcsharp_metadata_array_get_value_length_delegate grpcsharp_metadata_array_get_value_length;
+        public readonly Delegates.grpcsharp_metadata_array_destroy_full_delegate grpcsharp_metadata_array_destroy_full;
+
+        public readonly Delegates.grpcsharp_redirect_log_delegate grpcsharp_redirect_log;
+
+        public readonly Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate grpcsharp_metadata_credentials_create_from_plugin;
+        public readonly Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate grpcsharp_metadata_credentials_notify_from_plugin;
+
+        public readonly Delegates.grpcsharp_ssl_server_credentials_create_delegate grpcsharp_ssl_server_credentials_create;
+        public readonly Delegates.grpcsharp_server_credentials_release_delegate grpcsharp_server_credentials_release;
+
+        public readonly Delegates.grpcsharp_server_create_delegate grpcsharp_server_create;
+        public readonly Delegates.grpcsharp_server_add_insecure_http2_port_delegate grpcsharp_server_add_insecure_http2_port;
+        public readonly Delegates.grpcsharp_server_add_secure_http2_port_delegate grpcsharp_server_add_secure_http2_port;
+        public readonly Delegates.grpcsharp_server_start_delegate grpcsharp_server_start;
+        public readonly Delegates.grpcsharp_server_request_call_delegate grpcsharp_server_request_call;
+        public readonly Delegates.grpcsharp_server_cancel_all_calls_delegate grpcsharp_server_cancel_all_calls;
+        public readonly Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate grpcsharp_server_shutdown_and_notify_callback;
+        public readonly Delegates.grpcsharp_server_destroy_delegate grpcsharp_server_destroy;
+
+        public readonly Delegates.gprsharp_now_delegate gprsharp_now;
+        public readonly Delegates.gprsharp_inf_future_delegate gprsharp_inf_future;
+        public readonly Delegates.gprsharp_inf_past_delegate gprsharp_inf_past;
+        public readonly Delegates.gprsharp_convert_clock_type_delegate gprsharp_convert_clock_type;
+        public readonly Delegates.gprsharp_sizeof_timespec_delegate gprsharp_sizeof_timespec;
+
+        public readonly Delegates.grpcsharp_test_callback_delegate grpcsharp_test_callback;
+        public readonly Delegates.grpcsharp_test_nop_delegate grpcsharp_test_nop;
+
+        #endregion
+
+        public NativeMethods(UnmanagedLibrary library)
+        {
+            if (PlatformApis.IsLinux || PlatformApis.IsMacOSX)
+            {
+                this.grpcsharp_init = GetMethodDelegate<Delegates.grpcsharp_init_delegate>(library);
+                this.grpcsharp_shutdown = GetMethodDelegate<Delegates.grpcsharp_shutdown_delegate>(library);
+                this.grpcsharp_version_string = GetMethodDelegate<Delegates.grpcsharp_version_string_delegate>(library);
+
+                this.grpcsharp_batch_context_create = GetMethodDelegate<Delegates.grpcsharp_batch_context_create_delegate>(library);
+                this.grpcsharp_batch_context_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_initial_metadata_delegate>(library);
+                this.grpcsharp_batch_context_recv_message_length = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_length_delegate>(library);
+                this.grpcsharp_batch_context_recv_message_to_buffer = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_message_to_buffer_delegate>(library);
+                this.grpcsharp_batch_context_recv_status_on_client_status = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_status_delegate>(library);
+                this.grpcsharp_batch_context_recv_status_on_client_details = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_details_delegate>(library);
+                this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate>(library);
+                this.grpcsharp_batch_context_server_rpc_new_call = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_call_delegate>(library);
+                this.grpcsharp_batch_context_server_rpc_new_method = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_method_delegate>(library);
+                this.grpcsharp_batch_context_server_rpc_new_host = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_host_delegate>(library);
+                this.grpcsharp_batch_context_server_rpc_new_deadline = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_deadline_delegate>(library);
+                this.grpcsharp_batch_context_server_rpc_new_request_metadata = GetMethodDelegate<Delegates.grpcsharp_batch_context_server_rpc_new_request_metadata_delegate>(library);
+                this.grpcsharp_batch_context_recv_close_on_server_cancelled = GetMethodDelegate<Delegates.grpcsharp_batch_context_recv_close_on_server_cancelled_delegate>(library);
+                this.grpcsharp_batch_context_destroy = GetMethodDelegate<Delegates.grpcsharp_batch_context_destroy_delegate>(library);
+
+                this.grpcsharp_composite_call_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_call_credentials_create_delegate>(library);
+                this.grpcsharp_call_credentials_release = GetMethodDelegate<Delegates.grpcsharp_call_credentials_release_delegate>(library);
+
+                this.grpcsharp_call_cancel = GetMethodDelegate<Delegates.grpcsharp_call_cancel_delegate>(library);
+                this.grpcsharp_call_cancel_with_status = GetMethodDelegate<Delegates.grpcsharp_call_cancel_with_status_delegate>(library);
+                this.grpcsharp_call_start_unary = GetMethodDelegate<Delegates.grpcsharp_call_start_unary_delegate>(library);
+                this.grpcsharp_call_start_client_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_client_streaming_delegate>(library);
+                this.grpcsharp_call_start_server_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_server_streaming_delegate>(library);
+                this.grpcsharp_call_start_duplex_streaming = GetMethodDelegate<Delegates.grpcsharp_call_start_duplex_streaming_delegate>(library);
+                this.grpcsharp_call_send_message = GetMethodDelegate<Delegates.grpcsharp_call_send_message_delegate>(library);
+                this.grpcsharp_call_send_close_from_client = GetMethodDelegate<Delegates.grpcsharp_call_send_close_from_client_delegate>(library);
+                this.grpcsharp_call_send_status_from_server = GetMethodDelegate<Delegates.grpcsharp_call_send_status_from_server_delegate>(library);
+                this.grpcsharp_call_recv_message = GetMethodDelegate<Delegates.grpcsharp_call_recv_message_delegate>(library);
+                this.grpcsharp_call_recv_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_recv_initial_metadata_delegate>(library);
+                this.grpcsharp_call_start_serverside = GetMethodDelegate<Delegates.grpcsharp_call_start_serverside_delegate>(library);
+                this.grpcsharp_call_send_initial_metadata = GetMethodDelegate<Delegates.grpcsharp_call_send_initial_metadata_delegate>(library);
+                this.grpcsharp_call_set_credentials = GetMethodDelegate<Delegates.grpcsharp_call_set_credentials_delegate>(library);
+                this.grpcsharp_call_get_peer = GetMethodDelegate<Delegates.grpcsharp_call_get_peer_delegate>(library);
+                this.grpcsharp_call_destroy = GetMethodDelegate<Delegates.grpcsharp_call_destroy_delegate>(library);
+
+                this.grpcsharp_channel_args_create = GetMethodDelegate<Delegates.grpcsharp_channel_args_create_delegate>(library);
+                this.grpcsharp_channel_args_set_string = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_string_delegate>(library);
+                this.grpcsharp_channel_args_set_integer = GetMethodDelegate<Delegates.grpcsharp_channel_args_set_integer_delegate>(library);
+                this.grpcsharp_channel_args_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_args_destroy_delegate>(library);
+
+                this.grpcsharp_ssl_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_credentials_create_delegate>(library);
+                this.grpcsharp_composite_channel_credentials_create = GetMethodDelegate<Delegates.grpcsharp_composite_channel_credentials_create_delegate>(library);
+                this.grpcsharp_channel_credentials_release = GetMethodDelegate<Delegates.grpcsharp_channel_credentials_release_delegate>(library);
+
+                this.grpcsharp_insecure_channel_create = GetMethodDelegate<Delegates.grpcsharp_insecure_channel_create_delegate>(library);
+                this.grpcsharp_secure_channel_create = GetMethodDelegate<Delegates.grpcsharp_secure_channel_create_delegate>(library);
+                this.grpcsharp_channel_create_call = GetMethodDelegate<Delegates.grpcsharp_channel_create_call_delegate>(library);
+                this.grpcsharp_channel_check_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_check_connectivity_state_delegate>(library);
+                this.grpcsharp_channel_watch_connectivity_state = GetMethodDelegate<Delegates.grpcsharp_channel_watch_connectivity_state_delegate>(library);
+                this.grpcsharp_channel_get_target = GetMethodDelegate<Delegates.grpcsharp_channel_get_target_delegate>(library);
+                this.grpcsharp_channel_destroy = GetMethodDelegate<Delegates.grpcsharp_channel_destroy_delegate>(library);
+
+                this.grpcsharp_sizeof_grpc_event = GetMethodDelegate<Delegates.grpcsharp_sizeof_grpc_event_delegate>(library);
+
+                this.grpcsharp_completion_queue_create = GetMethodDelegate<Delegates.grpcsharp_completion_queue_create_delegate>(library);
+                this.grpcsharp_completion_queue_shutdown = GetMethodDelegate<Delegates.grpcsharp_completion_queue_shutdown_delegate>(library);
+                this.grpcsharp_completion_queue_next = GetMethodDelegate<Delegates.grpcsharp_completion_queue_next_delegate>(library);
+                this.grpcsharp_completion_queue_pluck = GetMethodDelegate<Delegates.grpcsharp_completion_queue_pluck_delegate>(library);
+                this.grpcsharp_completion_queue_destroy = GetMethodDelegate<Delegates.grpcsharp_completion_queue_destroy_delegate>(library);
+
+                this.gprsharp_free = GetMethodDelegate<Delegates.gprsharp_free_delegate>(library);
+
+                this.grpcsharp_metadata_array_create = GetMethodDelegate<Delegates.grpcsharp_metadata_array_create_delegate>(library);
+                this.grpcsharp_metadata_array_add = GetMethodDelegate<Delegates.grpcsharp_metadata_array_add_delegate>(library);
+                this.grpcsharp_metadata_array_count = GetMethodDelegate<Delegates.grpcsharp_metadata_array_count_delegate>(library);
+                this.grpcsharp_metadata_array_get_key = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_key_delegate>(library);
+                this.grpcsharp_metadata_array_get_value = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_delegate>(library);
+                this.grpcsharp_metadata_array_get_value_length = GetMethodDelegate<Delegates.grpcsharp_metadata_array_get_value_length_delegate>(library);
+                this.grpcsharp_metadata_array_destroy_full = GetMethodDelegate<Delegates.grpcsharp_metadata_array_destroy_full_delegate>(library);
+
+                this.grpcsharp_redirect_log = GetMethodDelegate<Delegates.grpcsharp_redirect_log_delegate>(library);
+
+                this.grpcsharp_metadata_credentials_create_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_create_from_plugin_delegate>(library);
+                this.grpcsharp_metadata_credentials_notify_from_plugin = GetMethodDelegate<Delegates.grpcsharp_metadata_credentials_notify_from_plugin_delegate>(library);
+
+                this.grpcsharp_ssl_server_credentials_create = GetMethodDelegate<Delegates.grpcsharp_ssl_server_credentials_create_delegate>(library);
+                this.grpcsharp_server_credentials_release = GetMethodDelegate<Delegates.grpcsharp_server_credentials_release_delegate>(library);
+
+                this.grpcsharp_server_create = GetMethodDelegate<Delegates.grpcsharp_server_create_delegate>(library);
+                this.grpcsharp_server_add_insecure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_insecure_http2_port_delegate>(library);
+                this.grpcsharp_server_add_secure_http2_port = GetMethodDelegate<Delegates.grpcsharp_server_add_secure_http2_port_delegate>(library);
+                this.grpcsharp_server_start = GetMethodDelegate<Delegates.grpcsharp_server_start_delegate>(library);
+                this.grpcsharp_server_request_call = GetMethodDelegate<Delegates.grpcsharp_server_request_call_delegate>(library);
+                this.grpcsharp_server_cancel_all_calls = GetMethodDelegate<Delegates.grpcsharp_server_cancel_all_calls_delegate>(library);
+                this.grpcsharp_server_shutdown_and_notify_callback = GetMethodDelegate<Delegates.grpcsharp_server_shutdown_and_notify_callback_delegate>(library);
+                this.grpcsharp_server_destroy = GetMethodDelegate<Delegates.grpcsharp_server_destroy_delegate>(library);
+
+                this.gprsharp_now = GetMethodDelegate<Delegates.gprsharp_now_delegate>(library);
+                this.gprsharp_inf_future = GetMethodDelegate<Delegates.gprsharp_inf_future_delegate>(library);
+                this.gprsharp_inf_past = GetMethodDelegate<Delegates.gprsharp_inf_past_delegate>(library);
+                this.gprsharp_convert_clock_type = GetMethodDelegate<Delegates.gprsharp_convert_clock_type_delegate>(library);
+                this.gprsharp_sizeof_timespec = GetMethodDelegate<Delegates.gprsharp_sizeof_timespec_delegate>(library);
+
+                this.grpcsharp_test_callback = GetMethodDelegate<Delegates.grpcsharp_test_callback_delegate>(library);
+                this.grpcsharp_test_nop = GetMethodDelegate<Delegates.grpcsharp_test_nop_delegate>(library);
+            }
+            else
+            {
+                // Windows or fallback
+                this.grpcsharp_init = PInvokeMethods.grpcsharp_init;
+                this.grpcsharp_shutdown = PInvokeMethods.grpcsharp_shutdown;
+                this.grpcsharp_version_string = PInvokeMethods.grpcsharp_version_string;
+
+                this.grpcsharp_batch_context_create = PInvokeMethods.grpcsharp_batch_context_create;
+                this.grpcsharp_batch_context_recv_initial_metadata = PInvokeMethods.grpcsharp_batch_context_recv_initial_metadata;
+                this.grpcsharp_batch_context_recv_message_length = PInvokeMethods.grpcsharp_batch_context_recv_message_length;
+                this.grpcsharp_batch_context_recv_message_to_buffer = PInvokeMethods.grpcsharp_batch_context_recv_message_to_buffer;
+                this.grpcsharp_batch_context_recv_status_on_client_status = PInvokeMethods.grpcsharp_batch_context_recv_status_on_client_status;
+                this.grpcsharp_batch_context_recv_status_on_client_details = PInvokeMethods.grpcsharp_batch_context_recv_status_on_client_details;
+                this.grpcsharp_batch_context_recv_status_on_client_trailing_metadata = PInvokeMethods.grpcsharp_batch_context_recv_status_on_client_trailing_metadata;
+                this.grpcsharp_batch_context_server_rpc_new_call = PInvokeMethods.grpcsharp_batch_context_server_rpc_new_call;
+                this.grpcsharp_batch_context_server_rpc_new_method = PInvokeMethods.grpcsharp_batch_context_server_rpc_new_method;
+                this.grpcsharp_batch_context_server_rpc_new_host = PInvokeMethods.grpcsharp_batch_context_server_rpc_new_host;
+                this.grpcsharp_batch_context_server_rpc_new_deadline = PInvokeMethods.grpcsharp_batch_context_server_rpc_new_deadline;
+                this.grpcsharp_batch_context_server_rpc_new_request_metadata = PInvokeMethods.grpcsharp_batch_context_server_rpc_new_request_metadata;
+                this.grpcsharp_batch_context_recv_close_on_server_cancelled = PInvokeMethods.grpcsharp_batch_context_recv_close_on_server_cancelled;
+                this.grpcsharp_batch_context_destroy = PInvokeMethods.grpcsharp_batch_context_destroy;
+
+                this.grpcsharp_composite_call_credentials_create = PInvokeMethods.grpcsharp_composite_call_credentials_create;
+                this.grpcsharp_call_credentials_release = PInvokeMethods.grpcsharp_call_credentials_release;
+
+                this.grpcsharp_call_cancel = PInvokeMethods.grpcsharp_call_cancel;
+                this.grpcsharp_call_cancel_with_status = PInvokeMethods.grpcsharp_call_cancel_with_status;
+                this.grpcsharp_call_start_unary = PInvokeMethods.grpcsharp_call_start_unary;
+                this.grpcsharp_call_start_client_streaming = PInvokeMethods.grpcsharp_call_start_client_streaming;
+                this.grpcsharp_call_start_server_streaming = PInvokeMethods.grpcsharp_call_start_server_streaming;
+                this.grpcsharp_call_start_duplex_streaming = PInvokeMethods.grpcsharp_call_start_duplex_streaming;
+                this.grpcsharp_call_send_message = PInvokeMethods.grpcsharp_call_send_message;
+                this.grpcsharp_call_send_close_from_client = PInvokeMethods.grpcsharp_call_send_close_from_client;
+                this.grpcsharp_call_send_status_from_server = PInvokeMethods.grpcsharp_call_send_status_from_server;
+                this.grpcsharp_call_recv_message = PInvokeMethods.grpcsharp_call_recv_message;
+                this.grpcsharp_call_recv_initial_metadata = PInvokeMethods.grpcsharp_call_recv_initial_metadata;
+                this.grpcsharp_call_start_serverside = PInvokeMethods.grpcsharp_call_start_serverside;
+                this.grpcsharp_call_send_initial_metadata = PInvokeMethods.grpcsharp_call_send_initial_metadata;
+                this.grpcsharp_call_set_credentials = PInvokeMethods.grpcsharp_call_set_credentials;
+                this.grpcsharp_call_get_peer = PInvokeMethods.grpcsharp_call_get_peer;
+                this.grpcsharp_call_destroy = PInvokeMethods.grpcsharp_call_destroy;
+
+                this.grpcsharp_channel_args_create = PInvokeMethods.grpcsharp_channel_args_create;
+                this.grpcsharp_channel_args_set_string = PInvokeMethods.grpcsharp_channel_args_set_string;
+                this.grpcsharp_channel_args_set_integer = PInvokeMethods.grpcsharp_channel_args_set_integer;
+                this.grpcsharp_channel_args_destroy = PInvokeMethods.grpcsharp_channel_args_destroy;
+
+                this.grpcsharp_ssl_credentials_create = PInvokeMethods.grpcsharp_ssl_credentials_create;
+                this.grpcsharp_composite_channel_credentials_create = PInvokeMethods.grpcsharp_composite_channel_credentials_create;
+                this.grpcsharp_channel_credentials_release = PInvokeMethods.grpcsharp_channel_credentials_release;
+
+                this.grpcsharp_insecure_channel_create = PInvokeMethods.grpcsharp_insecure_channel_create;
+                this.grpcsharp_secure_channel_create = PInvokeMethods.grpcsharp_secure_channel_create;
+                this.grpcsharp_channel_create_call = PInvokeMethods.grpcsharp_channel_create_call;
+                this.grpcsharp_channel_check_connectivity_state = PInvokeMethods.grpcsharp_channel_check_connectivity_state;
+                this.grpcsharp_channel_watch_connectivity_state = PInvokeMethods.grpcsharp_channel_watch_connectivity_state;
+                this.grpcsharp_channel_get_target = PInvokeMethods.grpcsharp_channel_get_target;
+                this.grpcsharp_channel_destroy = PInvokeMethods.grpcsharp_channel_destroy;
+
+                this.grpcsharp_sizeof_grpc_event = PInvokeMethods.grpcsharp_sizeof_grpc_event;
+
+                this.grpcsharp_completion_queue_create = PInvokeMethods.grpcsharp_completion_queue_create;
+                this.grpcsharp_completion_queue_shutdown = PInvokeMethods.grpcsharp_completion_queue_shutdown;
+                this.grpcsharp_completion_queue_next = PInvokeMethods.grpcsharp_completion_queue_next;
+                this.grpcsharp_completion_queue_pluck = PInvokeMethods.grpcsharp_completion_queue_pluck;
+                this.grpcsharp_completion_queue_destroy = PInvokeMethods.grpcsharp_completion_queue_destroy;
+
+                this.gprsharp_free = PInvokeMethods.gprsharp_free;
+
+                this.grpcsharp_metadata_array_create = PInvokeMethods.grpcsharp_metadata_array_create;
+                this.grpcsharp_metadata_array_add = PInvokeMethods.grpcsharp_metadata_array_add;
+                this.grpcsharp_metadata_array_count = PInvokeMethods.grpcsharp_metadata_array_count;
+                this.grpcsharp_metadata_array_get_key = PInvokeMethods.grpcsharp_metadata_array_get_key;
+                this.grpcsharp_metadata_array_get_value = PInvokeMethods.grpcsharp_metadata_array_get_value;
+                this.grpcsharp_metadata_array_get_value_length = PInvokeMethods.grpcsharp_metadata_array_get_value_length;
+                this.grpcsharp_metadata_array_destroy_full = PInvokeMethods.grpcsharp_metadata_array_destroy_full;
+
+                this.grpcsharp_redirect_log = PInvokeMethods.grpcsharp_redirect_log;
+
+                this.grpcsharp_metadata_credentials_create_from_plugin = PInvokeMethods.grpcsharp_metadata_credentials_create_from_plugin;
+                this.grpcsharp_metadata_credentials_notify_from_plugin = PInvokeMethods.grpcsharp_metadata_credentials_notify_from_plugin;
+
+                this.grpcsharp_ssl_server_credentials_create = PInvokeMethods.grpcsharp_ssl_server_credentials_create;
+                this.grpcsharp_server_credentials_release = PInvokeMethods.grpcsharp_server_credentials_release;
+
+                this.grpcsharp_server_create = PInvokeMethods.grpcsharp_server_create;
+                this.grpcsharp_server_add_insecure_http2_port = PInvokeMethods.grpcsharp_server_add_insecure_http2_port;
+                this.grpcsharp_server_add_secure_http2_port = PInvokeMethods.grpcsharp_server_add_secure_http2_port;
+                this.grpcsharp_server_start = PInvokeMethods.grpcsharp_server_start;
+                this.grpcsharp_server_request_call = PInvokeMethods.grpcsharp_server_request_call;
+                this.grpcsharp_server_cancel_all_calls = PInvokeMethods.grpcsharp_server_cancel_all_calls;
+                this.grpcsharp_server_shutdown_and_notify_callback = PInvokeMethods.grpcsharp_server_shutdown_and_notify_callback;
+                this.grpcsharp_server_destroy = PInvokeMethods.grpcsharp_server_destroy;
+
+                this.gprsharp_now = PInvokeMethods.gprsharp_now;
+                this.gprsharp_inf_future = PInvokeMethods.gprsharp_inf_future;
+                this.gprsharp_inf_past = PInvokeMethods.gprsharp_inf_past;
+                this.gprsharp_convert_clock_type = PInvokeMethods.gprsharp_convert_clock_type;
+                this.gprsharp_sizeof_timespec = PInvokeMethods.gprsharp_sizeof_timespec;
+
+                this.grpcsharp_test_callback = PInvokeMethods.grpcsharp_test_callback;
+                this.grpcsharp_test_nop = PInvokeMethods.grpcsharp_test_nop;
+            }
+        }
+
+        /// <summary>
+        /// Gets singleton instance of this class.
+        /// </summary>
+        public static NativeMethods Get()
+        {
+            return NativeExtension.Get().NativeMethods;
+        }
+
+        static T GetMethodDelegate<T>(UnmanagedLibrary library)
+            where T : class
+        {
+            var methodName = RemoveStringSuffix(typeof(T).Name, "_delegate");
+            return library.GetNativeMethodDelegate<T>(methodName);
+        }
+
+        static string RemoveStringSuffix(string str, string toRemove)
+        {
+            if (!str.EndsWith(toRemove))
+            {
+                return str;
+            }
+            return str.Substring(0, str.Length - toRemove.Length);
+        }
+
+        /// <summary>
+        /// Delegate types for all published native methods. Declared under inner class to prevent scope pollution.
+        /// </summary>
+        public class Delegates
+        {
+            public delegate void grpcsharp_init_delegate();
+            public delegate void grpcsharp_shutdown_delegate();
+            public delegate IntPtr grpcsharp_version_string_delegate();  // returns not-owned const char*
+
+            public delegate BatchContextSafeHandle grpcsharp_batch_context_create_delegate();
+            public delegate IntPtr grpcsharp_batch_context_recv_initial_metadata_delegate(BatchContextSafeHandle ctx);
+            public delegate IntPtr grpcsharp_batch_context_recv_message_length_delegate(BatchContextSafeHandle ctx);
+            public delegate void grpcsharp_batch_context_recv_message_to_buffer_delegate(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
+            public delegate StatusCode grpcsharp_batch_context_recv_status_on_client_status_delegate(BatchContextSafeHandle ctx);
+            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_details_delegate(BatchContextSafeHandle ctx);  // returns const char*
+            public delegate IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata_delegate(BatchContextSafeHandle ctx);
+            public delegate CallSafeHandle grpcsharp_batch_context_server_rpc_new_call_delegate(BatchContextSafeHandle ctx);
+            public delegate IntPtr grpcsharp_batch_context_server_rpc_new_method_delegate(BatchContextSafeHandle ctx);  // returns const char*
+            public delegate IntPtr grpcsharp_batch_context_server_rpc_new_host_delegate(BatchContextSafeHandle ctx);  // returns const char*
+            public delegate Timespec grpcsharp_batch_context_server_rpc_new_deadline_delegate(BatchContextSafeHandle ctx);
+            public delegate IntPtr grpcsharp_batch_context_server_rpc_new_request_metadata_delegate(BatchContextSafeHandle ctx);
+            public delegate int grpcsharp_batch_context_recv_close_on_server_cancelled_delegate(BatchContextSafeHandle ctx);
+            public delegate void grpcsharp_batch_context_destroy_delegate(IntPtr ctx);
+
+            public delegate CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create_delegate(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2);
+            public delegate void grpcsharp_call_credentials_release_delegate(IntPtr credentials);
+
+            public delegate GRPCCallError grpcsharp_call_cancel_delegate(CallSafeHandle call);
+            public delegate GRPCCallError grpcsharp_call_cancel_with_status_delegate(CallSafeHandle call, StatusCode status, string description);
+            public delegate GRPCCallError grpcsharp_call_start_unary_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+            public delegate GRPCCallError grpcsharp_call_start_client_streaming_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+            public delegate GRPCCallError grpcsharp_call_start_server_streaming_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len,
+                MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+            public delegate GRPCCallError grpcsharp_call_start_duplex_streaming_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+            public delegate GRPCCallError grpcsharp_call_send_message_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
+            public delegate GRPCCallError grpcsharp_call_send_close_from_client_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+            public delegate GRPCCallError grpcsharp_call_send_status_from_server_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata);
+            public delegate GRPCCallError grpcsharp_call_recv_message_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+            public delegate GRPCCallError grpcsharp_call_recv_initial_metadata_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+            public delegate GRPCCallError grpcsharp_call_start_serverside_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+            public delegate GRPCCallError grpcsharp_call_send_initial_metadata_delegate(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+            public delegate GRPCCallError grpcsharp_call_set_credentials_delegate(CallSafeHandle call, CallCredentialsSafeHandle credentials);
+            public delegate CStringSafeHandle grpcsharp_call_get_peer_delegate(CallSafeHandle call);
+            public delegate void grpcsharp_call_destroy_delegate(IntPtr call);
+
+            public delegate ChannelArgsSafeHandle grpcsharp_channel_args_create_delegate(UIntPtr numArgs);
+            public delegate void grpcsharp_channel_args_set_string_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
+            public delegate void grpcsharp_channel_args_set_integer_delegate(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
+            public delegate void grpcsharp_channel_args_destroy_delegate(IntPtr args);
+
+            public delegate ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create_delegate(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+            public delegate ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create_delegate(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
+            public delegate void grpcsharp_channel_credentials_release_delegate(IntPtr credentials);
+
+            public delegate ChannelSafeHandle grpcsharp_insecure_channel_create_delegate(string target, ChannelArgsSafeHandle channelArgs);
+            public delegate ChannelSafeHandle grpcsharp_secure_channel_create_delegate(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
+            public delegate CallSafeHandle grpcsharp_channel_create_call_delegate(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
+            public delegate ChannelState grpcsharp_channel_check_connectivity_state_delegate(ChannelSafeHandle channel, int tryToConnect);
+            public delegate void grpcsharp_channel_watch_connectivity_state_delegate(ChannelSafeHandle channel, ChannelState lastObservedState,
+                Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+            public delegate CStringSafeHandle grpcsharp_channel_get_target_delegate(ChannelSafeHandle call);
+            public delegate void grpcsharp_channel_destroy_delegate(IntPtr channel);
+
+            public delegate int grpcsharp_sizeof_grpc_event_delegate();
+
+            public delegate CompletionQueueSafeHandle grpcsharp_completion_queue_create_delegate();
+            public delegate void grpcsharp_completion_queue_shutdown_delegate(CompletionQueueSafeHandle cq);
+            public delegate CompletionQueueEvent grpcsharp_completion_queue_next_delegate(CompletionQueueSafeHandle cq);
+            public delegate CompletionQueueEvent grpcsharp_completion_queue_pluck_delegate(CompletionQueueSafeHandle cq, IntPtr tag);
+            public delegate void grpcsharp_completion_queue_destroy_delegate(IntPtr cq);
+
+            public delegate void gprsharp_free_delegate(IntPtr ptr);
+
+            public delegate MetadataArraySafeHandle grpcsharp_metadata_array_create_delegate(UIntPtr capacity);
+            public delegate void grpcsharp_metadata_array_add_delegate(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength);
+            public delegate UIntPtr grpcsharp_metadata_array_count_delegate(IntPtr metadataArray);
+            public delegate IntPtr grpcsharp_metadata_array_get_key_delegate(IntPtr metadataArray, UIntPtr index);
+            public delegate IntPtr grpcsharp_metadata_array_get_value_delegate(IntPtr metadataArray, UIntPtr index);
+            public delegate UIntPtr grpcsharp_metadata_array_get_value_length_delegate(IntPtr metadataArray, UIntPtr index);
+            public delegate void grpcsharp_metadata_array_destroy_full_delegate(IntPtr array);
+
+            public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback);
+
+            public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor);
+            public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
+
+            public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth);
+            public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials);
+
+            public delegate ServerSafeHandle grpcsharp_server_create_delegate(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args);
+            public delegate int grpcsharp_server_add_insecure_http2_port_delegate(ServerSafeHandle server, string addr);
+            public delegate int grpcsharp_server_add_secure_http2_port_delegate(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds);
+            public delegate void grpcsharp_server_start_delegate(ServerSafeHandle server);
+            public delegate GRPCCallError grpcsharp_server_request_call_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+            public delegate void grpcsharp_server_cancel_all_calls_delegate(ServerSafeHandle server);
+            public delegate void grpcsharp_server_shutdown_and_notify_callback_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+            public delegate void grpcsharp_server_destroy_delegate(IntPtr server);
+
+            public delegate Timespec gprsharp_now_delegate(GPRClockType clockType);
+            public delegate Timespec gprsharp_inf_future_delegate(GPRClockType clockType);
+            public delegate Timespec gprsharp_inf_past_delegate(GPRClockType clockType);
+
+            public delegate Timespec gprsharp_convert_clock_type_delegate(Timespec t, GPRClockType targetClock);
+            public delegate int gprsharp_sizeof_timespec_delegate();
+
+            public delegate GRPCCallError grpcsharp_test_callback_delegate([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
+            public delegate IntPtr grpcsharp_test_nop_delegate(IntPtr ptr);
+        }
+
+        /// <summary>
+        /// Default PInvoke bindings for native methods that are used on Windows.
+        /// Alternatively, they can also be used as a fallback on Mono
+        /// (if libgrpc_csharp_ext is installed on your system, or is made accessible through e.g. LD_LIBRARY_PATH environment variable
+        /// or using Mono's dllMap feature).
+        /// </summary>
+        private class PInvokeMethods
+        {
+            // Environment
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_init();
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_shutdown();
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_version_string();  // returns not-owned const char*
+
+            // BatchContextSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern BatchContextSafeHandle grpcsharp_batch_context_create();
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_recv_initial_metadata(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_recv_message_length(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_batch_context_recv_message_to_buffer(BatchContextSafeHandle ctx, byte[] buffer, UIntPtr bufferLen);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern StatusCode grpcsharp_batch_context_recv_status_on_client_status(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_details(BatchContextSafeHandle ctx);  // returns const char*
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_recv_status_on_client_trailing_metadata(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CallSafeHandle grpcsharp_batch_context_server_rpc_new_call(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_server_rpc_new_method(BatchContextSafeHandle ctx);  // returns const char*
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_server_rpc_new_host(BatchContextSafeHandle ctx);  // returns const char*
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern Timespec grpcsharp_batch_context_server_rpc_new_deadline(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_batch_context_server_rpc_new_request_metadata(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern int grpcsharp_batch_context_recv_close_on_server_cancelled(BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_batch_context_destroy(IntPtr ctx);
+
+            // CallCredentialsSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CallCredentialsSafeHandle grpcsharp_composite_call_credentials_create(CallCredentialsSafeHandle creds1, CallCredentialsSafeHandle creds2);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_call_credentials_release(IntPtr credentials);
+
+            // CallSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_cancel(CallSafeHandle call);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_start_unary(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_start_client_streaming(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_start_server_streaming(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len,
+                MetadataArraySafeHandle metadataArray, WriteFlags writeFlags);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_start_duplex_streaming(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_send_message(CallSafeHandle call,
+                BatchContextSafeHandle ctx, byte[] send_buffer, UIntPtr send_buffer_len, WriteFlags writeFlags, bool sendEmptyInitialMetadata);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_send_close_from_client(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_send_status_from_server(CallSafeHandle call,
+                BatchContextSafeHandle ctx, StatusCode statusCode, string statusMessage, MetadataArraySafeHandle metadataArray, bool sendEmptyInitialMetadata);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_recv_message(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_recv_initial_metadata(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_start_serverside(CallSafeHandle call,
+                BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_send_initial_metadata(CallSafeHandle call,
+                BatchContextSafeHandle ctx, MetadataArraySafeHandle metadataArray);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_call_set_credentials(CallSafeHandle call, CallCredentialsSafeHandle credentials);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CStringSafeHandle grpcsharp_call_get_peer(CallSafeHandle call);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_call_destroy(IntPtr call);
+
+            // ChannelArgsSafeHandle 
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ChannelArgsSafeHandle grpcsharp_channel_args_create(UIntPtr numArgs);
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern void grpcsharp_channel_args_set_string(ChannelArgsSafeHandle args, UIntPtr index, string key, string value);
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern void grpcsharp_channel_args_set_integer(ChannelArgsSafeHandle args, UIntPtr index, string key, int value);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_channel_args_destroy(IntPtr args);
+
+            // ChannelCredentialsSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern ChannelCredentialsSafeHandle grpcsharp_ssl_credentials_create(string pemRootCerts, string keyCertPairCertChain, string keyCertPairPrivateKey);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ChannelCredentialsSafeHandle grpcsharp_composite_channel_credentials_create(ChannelCredentialsSafeHandle channelCreds, CallCredentialsSafeHandle callCreds);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_channel_credentials_release(IntPtr credentials);
+
+            // ChannelSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ChannelSafeHandle grpcsharp_insecure_channel_create(string target, ChannelArgsSafeHandle channelArgs);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ChannelSafeHandle grpcsharp_secure_channel_create(ChannelCredentialsSafeHandle credentials, string target, ChannelArgsSafeHandle channelArgs);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CallSafeHandle grpcsharp_channel_create_call(ChannelSafeHandle channel, CallSafeHandle parentCall, ContextPropagationFlags propagationMask, CompletionQueueSafeHandle cq, string method, string host, Timespec deadline);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ChannelState grpcsharp_channel_check_connectivity_state(ChannelSafeHandle channel, int tryToConnect);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_channel_watch_connectivity_state(ChannelSafeHandle channel, ChannelState lastObservedState,
+                Timespec deadline, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CStringSafeHandle grpcsharp_channel_get_target(ChannelSafeHandle call);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_channel_destroy(IntPtr channel);
+
+            // CompletionQueueEvent
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern int grpcsharp_sizeof_grpc_event();
+
+            // CompletionQueueSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CompletionQueueSafeHandle grpcsharp_completion_queue_create();
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_completion_queue_shutdown(CompletionQueueSafeHandle cq);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CompletionQueueEvent grpcsharp_completion_queue_next(CompletionQueueSafeHandle cq);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CompletionQueueEvent grpcsharp_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_completion_queue_destroy(IntPtr cq);
+
+            // CStringSafeHandle 
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void gprsharp_free(IntPtr ptr);
+
+            // MetadataArraySafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern MetadataArraySafeHandle grpcsharp_metadata_array_create(UIntPtr capacity);
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern void grpcsharp_metadata_array_add(MetadataArraySafeHandle array, string key, byte[] value, UIntPtr valueLength);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern UIntPtr grpcsharp_metadata_array_count(IntPtr metadataArray);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_metadata_array_get_key(IntPtr metadataArray, UIntPtr index);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_metadata_array_get_value(IntPtr metadataArray, UIntPtr index);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern UIntPtr grpcsharp_metadata_array_get_value_length(IntPtr metadataArray, UIntPtr index);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_metadata_array_destroy_full(IntPtr array);
+
+            // NativeLogRedirector
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_redirect_log(GprLogDelegate callback);
+
+            // NativeMetadataCredentialsPlugin
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor);
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
+
+            // ServerCredentialsSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
+            public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_server_credentials_release(IntPtr credentials);
+
+            // ServerSafeHandle
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_server_start(ServerSafeHandle server);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern void grpcsharp_server_destroy(IntPtr server);
+
+            // Timespec
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern Timespec gprsharp_now(GPRClockType clockType);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern Timespec gprsharp_inf_future(GPRClockType clockType);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern Timespec gprsharp_inf_past(GPRClockType clockType);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern Timespec gprsharp_convert_clock_type(Timespec t, GPRClockType targetClock);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern int gprsharp_sizeof_timespec();
+
+            // Testing
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern GRPCCallError grpcsharp_test_callback([MarshalAs(UnmanagedType.FunctionPtr)] OpCompletionDelegate callback);
+
+            [DllImport("grpc_csharp_ext.dll")]
+            public static extern IntPtr grpcsharp_test_nop(IntPtr ptr);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/PlatformApis.cs b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
new file mode 100644
index 0000000..e6c5765
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/PlatformApis.cs
@@ -0,0 +1,110 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Utility methods for detecting platform and architecture.
+    /// </summary>
+    internal static class PlatformApis
+    {
+        static readonly bool isLinux;
+        static readonly bool isMacOSX;
+        static readonly bool isWindows;
+
+        static PlatformApis()
+        {
+            var platform = Environment.OSVersion.Platform;
+
+            // PlatformID.MacOSX is never returned, commonly used trick is to identify Mac is by using uname.
+            isMacOSX = (platform == PlatformID.Unix && GetUname() == "Darwin");
+            isLinux = (platform == PlatformID.Unix && !isMacOSX);
+            isWindows = (platform == PlatformID.Win32NT || platform == PlatformID.Win32S || platform == PlatformID.Win32Windows);
+        }
+
+        public static bool IsLinux
+        {
+            get { return isLinux; }
+        }
+
+        public static bool IsMacOSX
+        {
+            get { return isMacOSX; }
+        }
+
+        public static bool IsWindows
+        {
+            get { return isWindows; }
+        }
+
+        public static bool Is64Bit
+        {
+            get { return IntPtr.Size == 8; }
+        }
+
+        [DllImport("libc")]
+        static extern int uname(IntPtr buf);
+
+        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);
+                }
+            }
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
index 51e352a..d50e1c7 100644
--- a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
@@ -41,11 +41,7 @@
     /// </summary>
     internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)]
-        static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, bool forceClientAuth);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_credentials_release(IntPtr credentials);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private ServerCredentialsSafeHandle()
         {
@@ -54,15 +50,15 @@
         public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, bool forceClientAuth)
         {
             Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length);
-            return grpcsharp_ssl_server_credentials_create(pemRootCerts,
-                                                           keyCertPairCertChainArray, keyCertPairPrivateKeyArray,
-                                                           new UIntPtr((ulong)keyCertPairCertChainArray.Length),
-                                                           forceClientAuth);
+            return Native.grpcsharp_ssl_server_credentials_create(pemRootCerts,
+                                                                  keyCertPairCertChainArray, keyCertPairPrivateKeyArray,
+                                                                  new UIntPtr((ulong)keyCertPairCertChainArray.Length),
+                                                                  forceClientAuth);
         }
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_server_credentials_release(handle);
+            Native.grpcsharp_server_credentials_release(handle);
             return true;
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
index 5ee7ac1..6b5f70e 100644
--- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
+++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs
@@ -44,29 +44,7 @@
     /// </summary>
     internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid
     {
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern ServerSafeHandle grpcsharp_server_create(CompletionQueueSafeHandle cq, ChannelArgsSafeHandle args);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern int grpcsharp_server_add_insecure_http2_port(ServerSafeHandle server, string addr);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern int grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_start(ServerSafeHandle server);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern GRPCCallError grpcsharp_server_request_call(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_cancel_all_calls(ServerSafeHandle server);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_shutdown_and_notify_callback(ServerSafeHandle server, CompletionQueueSafeHandle cq, BatchContextSafeHandle ctx);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern void grpcsharp_server_destroy(IntPtr server);
+        static readonly NativeMethods Native = NativeMethods.Get();
 
         private ServerSafeHandle()
         {
@@ -77,41 +55,41 @@
             // Increment reference count for the native gRPC environment to make sure we don't do grpc_shutdown() before destroying the server handle.
             // Doing so would make object finalizer crash if we end up abandoning the handle.
             GrpcEnvironment.GrpcNativeInit();
-            return grpcsharp_server_create(cq, args);
+            return Native.grpcsharp_server_create(cq, args);
         }
 
         public int AddInsecurePort(string addr)
         {
-            return grpcsharp_server_add_insecure_http2_port(this, addr);
+            return Native.grpcsharp_server_add_insecure_http2_port(this, addr);
         }
 
         public int AddSecurePort(string addr, ServerCredentialsSafeHandle credentials)
         {
-            return grpcsharp_server_add_secure_http2_port(this, addr, credentials);
+            return Native.grpcsharp_server_add_secure_http2_port(this, addr, credentials);
         }
 
         public void Start()
         {
-            grpcsharp_server_start(this);
+            Native.grpcsharp_server_start(this);
         }
     
         public void ShutdownAndNotify(BatchCompletionDelegate callback, GrpcEnvironment environment)
         {
             var ctx = BatchContextSafeHandle.Create();
             environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
-            grpcsharp_server_shutdown_and_notify_callback(this, environment.CompletionQueue, ctx);
+            Native.grpcsharp_server_shutdown_and_notify_callback(this, environment.CompletionQueue, ctx);
         }
 
         public void RequestCall(BatchCompletionDelegate callback, GrpcEnvironment environment)
         {
             var ctx = BatchContextSafeHandle.Create();
             environment.CompletionRegistry.RegisterBatchCompletion(ctx, callback);
-            grpcsharp_server_request_call(this, environment.CompletionQueue, ctx).CheckOk();
+            Native.grpcsharp_server_request_call(this, environment.CompletionQueue, ctx).CheckOk();
         }
 
         protected override bool ReleaseHandle()
         {
-            grpcsharp_server_destroy(handle);
+            Native.grpcsharp_server_destroy(handle);
             GrpcEnvironment.GrpcNativeShutdown();
             return true;
         }
@@ -119,7 +97,7 @@
         // Only to be called after ShutdownAndNotify.
         public void CancelAllCalls()
         {
-            grpcsharp_server_cancel_all_calls(this);
+            Native.grpcsharp_server_cancel_all_calls(this);
         }
     }
 }
diff --git a/src/csharp/Grpc.Core/Internal/Timespec.cs b/src/csharp/Grpc.Core/Internal/Timespec.cs
index 3031175..18bcd8c 100644
--- a/src/csharp/Grpc.Core/Internal/Timespec.cs
+++ b/src/csharp/Grpc.Core/Internal/Timespec.cs
@@ -46,23 +46,9 @@
         const long NanosPerTick = 100;
         const long TicksPerSecond = NanosPerSecond / NanosPerTick;
 
+        static readonly NativeMethods Native = NativeMethods.Get();
         static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
 
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern Timespec gprsharp_now(GPRClockType clockType);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern Timespec gprsharp_inf_future(GPRClockType clockType);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern Timespec gprsharp_inf_past(GPRClockType clockType);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern Timespec gprsharp_convert_clock_type(Timespec t, GPRClockType targetClock);
-
-        [DllImport("grpc_csharp_ext.dll")]
-        static extern int gprsharp_sizeof_timespec();
-
         public Timespec(long tv_sec, int tv_nsec) : this(tv_sec, tv_nsec, GPRClockType.Realtime)
         {
         }
@@ -85,7 +71,7 @@
         {
             get
             {
-                return gprsharp_inf_future(GPRClockType.Realtime);
+                return Native.gprsharp_inf_future(GPRClockType.Realtime);
             }
         }
 
@@ -96,7 +82,7 @@
         {
             get
             {
-                return gprsharp_inf_past(GPRClockType.Realtime);
+                return Native.gprsharp_inf_past(GPRClockType.Realtime);
             }
         }
 
@@ -107,7 +93,7 @@
         {
             get
             {
-                return gprsharp_now(GPRClockType.Realtime);
+                return Native.gprsharp_now(GPRClockType.Realtime);
             }
         }
 
@@ -138,7 +124,7 @@
         /// </summary>
         public Timespec ToClockType(GPRClockType targetClock)
         {
-            return gprsharp_convert_clock_type(this, targetClock);
+            return Native.gprsharp_convert_clock_type(this, targetClock);
         }
             
         /// <summary>
@@ -241,7 +227,7 @@
         {
             get
             {
-                return gprsharp_now(GPRClockType.Precise);
+                return Native.gprsharp_now(GPRClockType.Precise);
             }
         }
 
@@ -249,7 +235,7 @@
         {
             get
             {
-                return gprsharp_sizeof_timespec();
+                return Native.gprsharp_sizeof_timespec();
             }
         }
     }
diff --git a/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
new file mode 100644
index 0000000..9a8b8ab
--- /dev/null
+++ b/src/csharp/Grpc.Core/Internal/UnmanagedLibrary.cs
@@ -0,0 +1,158 @@
+#region Copyright notice and license
+
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using System;
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using Grpc.Core.Logging;
+using Grpc.Core.Utils;
+
+namespace Grpc.Core.Internal
+{
+    /// <summary>
+    /// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner.
+    /// An important difference in library loading semantics is that on Windows, once we load a dynamic library using LoadLibrary,
+    /// that library becomes instantly available for <c>DllImport</c> P/Invoke calls referring to the same library name.
+    /// On Unix systems, dlopen has somewhat different semantics, so we need to use dlsym and <c>Marshal.GetDelegateForFunctionPointer</c>
+    /// to obtain delegates to native methods.
+    /// See http://stackoverflow.com/questions/13461989/p-invoke-to-dynamically-loaded-library-on-mono.
+    /// </summary>
+    internal class UnmanagedLibrary
+    {
+        static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<UnmanagedLibrary>();
+
+        // flags for dlopen
+        const int RTLD_LAZY = 1;
+        const int RTLD_GLOBAL = 8;
+
+        readonly string libraryPath;
+        readonly IntPtr handle;
+
+        public UnmanagedLibrary(string libraryPath)
+        {
+            this.libraryPath = Preconditions.CheckNotNull(libraryPath);
+
+            if (!File.Exists(this.libraryPath))
+            {
+                throw new FileNotFoundException("Error loading native library. File does not exist.", this.libraryPath);
+            }
+
+            Logger.Debug("Attempting to load native library \"{0}\"", this.libraryPath);
+
+            this.handle = PlatformSpecificLoadLibrary(this.libraryPath);
+
+            if (this.handle == IntPtr.Zero)
+            {
+                throw new IOException(string.Format("Error loading native library \"{0}\"", this.libraryPath));
+            }
+        }
+
+        /// <summary>
+        /// Loads symbol in a platform specific way.
+        /// </summary>
+        /// <param name="symbolName"></param>
+        /// <returns></returns>
+        public IntPtr LoadSymbol(string symbolName)
+        {
+            if (PlatformApis.IsLinux)
+            {
+                return Linux.dlsym(this.handle, symbolName);
+            }
+            if (PlatformApis.IsMacOSX)
+            {
+                return MacOSX.dlsym(this.handle, symbolName);
+            }
+            throw new InvalidOperationException("Unsupported platform.");
+        }
+
+        public T GetNativeMethodDelegate<T>(string methodName)
+            where T : class
+        {
+            var ptr = LoadSymbol(methodName);
+            if (ptr == IntPtr.Zero)
+            {
+                throw new MissingMethodException(string.Format("The native method \"{0}\" does not exist", methodName));
+            }
+            return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T;
+        }
+
+        /// <summary>
+        /// Loads library in a platform specific way.
+        /// </summary>
+        private static IntPtr PlatformSpecificLoadLibrary(string libraryPath)
+        {
+            if (PlatformApis.IsWindows)
+            {
+                return Windows.LoadLibrary(libraryPath);
+            }
+            if (PlatformApis.IsLinux)
+            {
+                return Linux.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
+            }
+            if (PlatformApis.IsMacOSX)
+            {
+                return MacOSX.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
+            }
+            throw new InvalidOperationException("Unsupported platform.");
+        }
+
+        private static class Windows
+        {
+            [DllImport("kernel32.dll")]
+            internal static extern IntPtr LoadLibrary(string filename);
+        }
+
+        private static class Linux
+        {
+            [DllImport("libdl.so")]
+            internal static extern IntPtr dlopen(string filename, int flags);
+
+            [DllImport("libdl.so")]
+            internal static extern IntPtr dlsym(IntPtr handle, string symbol);
+        }
+
+        private static class MacOSX
+        {
+            [DllImport("libSystem.dylib")]
+            internal static extern IntPtr dlopen(string filename, int flags);
+
+            [DllImport("libSystem.dylib")]
+            internal static extern IntPtr dlsym(IntPtr handle, string symbol);
+        }
+    }
+}
diff --git a/src/csharp/Grpc.Core/NativeDeps.Linux.targets b/src/csharp/Grpc.Core/NativeDeps.Linux.targets
new file mode 100644
index 0000000..a3848c6
--- /dev/null
+++ b/src/csharp/Grpc.Core/NativeDeps.Linux.targets
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Content Include="..\..\..\libs\$(NativeDependenciesConfigurationUnix)\libgrpc_csharp_ext.so">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <Link>nativelibs\linux_x64\libgrpc_csharp_ext.so</Link>
+    </Content>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/NativeDeps.Mac.targets b/src/csharp/Grpc.Core/NativeDeps.Mac.targets
new file mode 100644
index 0000000..c3c6264
--- /dev/null
+++ b/src/csharp/Grpc.Core/NativeDeps.Mac.targets
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Content Include="..\..\..\libs\$(NativeDependenciesConfigurationUnix)\libgrpc_csharp_ext.dylib">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <Link>nativelibs\macosx_x86\libgrpc_csharp_ext.dylib</Link>
+    </Content>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/NativeDeps.Windows.targets b/src/csharp/Grpc.Core/NativeDeps.Windows.targets
new file mode 100644
index 0000000..f6a3405
--- /dev/null
+++ b/src/csharp/Grpc.Core/NativeDeps.Windows.targets
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Content Include="..\..\..\vsprojects\$(NativeDependenciesConfiguration)\grpc_csharp_ext.dll">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+      <Link>nativelibs\windows_x86\grpc_csharp_ext.dll</Link>
+    </Content>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/src/csharp/Grpc.Core/NativeDeps.targets b/src/csharp/Grpc.Core/NativeDeps.targets
new file mode 100644
index 0000000..66c5ec1
--- /dev/null
+++ b/src/csharp/Grpc.Core/NativeDeps.targets
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+
+  <PropertyGroup Condition=" '$(NativeDependenciesConfiguration)' == '' ">
+    <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'Debug' ">Debug</NativeDependenciesConfiguration>
+    <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'Release' ">Release</NativeDependenciesConfiguration>
+    <NativeDependenciesConfiguration Condition=" '$(Configuration)' == 'ReleaseSigned' ">Release</NativeDependenciesConfiguration>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(NativeDependenciesConfigurationUnix)' == '' ">
+    <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'Debug' ">dbg</NativeDependenciesConfigurationUnix>
+    <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'Release' ">opt</NativeDependenciesConfigurationUnix>
+    <NativeDependenciesConfigurationUnix Condition=" '$(Configuration)' == 'ReleaseSigned' ">opt</NativeDependenciesConfigurationUnix>
+  </PropertyGroup>
+
+  <!-- Autodetect platform -->
+  <PropertyGroup Condition=" '$(OS)' != 'Unix' ">
+    <NativeDepsPlatform>Windows</NativeDepsPlatform>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(OS)' == 'Unix' And Exists('/Applications') And Exists('/Library') And Exists('/System') ">
+    <NativeDepsPlatform>Mac</NativeDepsPlatform>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(OS)' == 'Unix' And '$(NativeDepsPlatform)' == '' ">
+    <NativeDepsPlatform>Linux</NativeDepsPlatform>
+  </PropertyGroup>
+
+  <Import Project="NativeDeps.$(NativeDepsPlatform).targets" />
+</Project>
\ No newline at end of file
diff --git a/tools/jenkins/grpc_interop_csharp/build_interop.sh b/tools/jenkins/grpc_interop_csharp/build_interop.sh
index 8fde687..88d44df 100755
--- a/tools/jenkins/grpc_interop_csharp/build_interop.sh
+++ b/tools/jenkins/grpc_interop_csharp/build_interop.sh
@@ -42,6 +42,6 @@
 make install-certs
 
 # build C# interop client & server
-make install_grpc_csharp_ext
+make CONFIG=dbg grpc_csharp_ext
 (cd src/csharp && mono /var/local/NuGet.exe restore Grpc.sln)
 (cd src/csharp && xbuild Grpc.sln)
diff --git a/tools/run_tests/build_csharp.sh b/tools/run_tests/build_csharp.sh
index 55643ff..48ce11a 100755
--- a/tools/run_tests/build_csharp.sh
+++ b/tools/run_tests/build_csharp.sh
@@ -32,4 +32,5 @@
 
 cd $(dirname $0)/../../src/csharp
 
-xbuild /p:Configuration=$MSBUILD_CONFIG Grpc.sln
+# overriding NativeDependenciesConfigurationUnix is needed to make gcov code coverage work.
+xbuild /p:Configuration=$MSBUILD_CONFIG /p:NativeDependenciesConfigurationUnix=$CONFIG Grpc.sln
diff --git a/tools/run_tests/run_csharp.sh b/tools/run_tests/run_csharp.sh
index 744df07..dad44c4 100755
--- a/tools/run_tests/run_csharp.sh
+++ b/tools/run_tests/run_csharp.sh
@@ -36,9 +36,6 @@
 # change to gRPC repo root
 cd $(dirname $0)/../..
 
-# path needs to be absolute
-export LD_LIBRARY_PATH=$(pwd)/libs/$CONFIG
-
 (cd src/csharp; $NUNIT_CONSOLE $@)
 
 if [ "$CONFIG" = "gcov" ]