|  | @@ -51,11 +51,12 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              Logger.Debug("Attempting to load native library \"{0}\"", this.libraryPath);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            this.handle = PlatformSpecificLoadLibrary(this.libraryPath);
 | 
	
		
			
				|  |  | +            this.handle = PlatformSpecificLoadLibrary(this.libraryPath, out string loadLibraryErrorDetail);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if (this.handle == IntPtr.Zero)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                throw new IOException(string.Format("Error loading native library \"{0}\"", this.libraryPath));
 | 
	
		
			
				|  |  | +                throw new IOException(string.Format("Error loading native library \"{0}\". {1}",
 | 
	
		
			
				|  |  | +                                                    this.libraryPath, loadLibraryErrorDetail));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -129,31 +130,44 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Loads library in a platform specific way.
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  | -        private static IntPtr PlatformSpecificLoadLibrary(string libraryPath)
 | 
	
		
			
				|  |  | +        private static IntPtr PlatformSpecificLoadLibrary(string libraryPath, out string errorMsg)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (PlatformApis.IsWindows)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                // TODO(jtattermusch): populate the error on Windows
 | 
	
		
			
				|  |  | +                errorMsg = null;
 | 
	
		
			
				|  |  |                  return Windows.LoadLibrary(libraryPath);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              if (PlatformApis.IsLinux)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  if (PlatformApis.IsMono)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    return Mono.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
 | 
	
		
			
				|  |  | +                    return LoadLibraryPosix(Mono.dlopen, Mono.dlerror, libraryPath, out errorMsg);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |                  if (PlatformApis.IsNetCore)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    return CoreCLR.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
 | 
	
		
			
				|  |  | +                    return LoadLibraryPosix(CoreCLR.dlopen, CoreCLR.dlerror, libraryPath, out errorMsg);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                return Linux.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
 | 
	
		
			
				|  |  | +                return LoadLibraryPosix(Linux.dlopen, Linux.dlerror, libraryPath, out errorMsg);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              if (PlatformApis.IsMacOSX)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                return MacOSX.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
 | 
	
		
			
				|  |  | +                return LoadLibraryPosix(MacOSX.dlopen, MacOSX.dlerror, libraryPath, out errorMsg);
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              throw new InvalidOperationException("Unsupported platform.");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        private static IntPtr LoadLibraryPosix(Func<string, int, IntPtr> dlopenFunc, Func<IntPtr> dlerrorFunc, string libraryPath, out string errorMsg)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            errorMsg = null;
 | 
	
		
			
				|  |  | +            IntPtr ret = dlopenFunc(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
 | 
	
		
			
				|  |  | +            if (ret == IntPtr.Zero)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                errorMsg = Marshal.PtrToStringAnsi(dlerrorFunc());
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return ret;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private static string FirstValidLibraryPath(string[] libraryPathAlternatives)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              GrpcPreconditions.CheckArgument(libraryPathAlternatives.Length > 0, "libraryPathAlternatives cannot be empty.");
 | 
	
	
		
			
				|  | @@ -183,6 +197,9 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |              [DllImport("libdl.so")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlopen(string filename, int flags);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            [DllImport("libdl.so")]
 | 
	
		
			
				|  |  | +            internal static extern IntPtr dlerror();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              [DllImport("libdl.so")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlsym(IntPtr handle, string symbol);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -192,6 +209,9 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |              [DllImport("libSystem.dylib")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlopen(string filename, int flags);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            [DllImport("libSystem.dylib")]
 | 
	
		
			
				|  |  | +            internal static extern IntPtr dlerror();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              [DllImport("libSystem.dylib")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlsym(IntPtr handle, string symbol);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -208,6 +228,9 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |              [DllImport("__Internal")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlopen(string filename, int flags);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            [DllImport("__Internal")]
 | 
	
		
			
				|  |  | +            internal static extern IntPtr dlerror();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              [DllImport("__Internal")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlsym(IntPtr handle, string symbol);
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -222,6 +245,9 @@ namespace Grpc.Core.Internal
 | 
	
		
			
				|  |  |              [DllImport("libcoreclr.so")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlopen(string filename, int flags);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +            [DllImport("libcoreclr.so")]
 | 
	
		
			
				|  |  | +            internal static extern IntPtr dlerror();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              [DllImport("libcoreclr.so")]
 | 
	
		
			
				|  |  |              internal static extern IntPtr dlsym(IntPtr handle, string symbol);
 | 
	
		
			
				|  |  |          }
 |