UnmanagedLibrary.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #region Copyright notice and license
  2. // Copyright 2015, Google Inc.
  3. // All rights reserved.
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are
  7. // met:
  8. //
  9. // * Redistributions of source code must retain the above copyright
  10. // notice, this list of conditions and the following disclaimer.
  11. // * Redistributions in binary form must reproduce the above
  12. // copyright notice, this list of conditions and the following disclaimer
  13. // in the documentation and/or other materials provided with the
  14. // distribution.
  15. // * Neither the name of Google Inc. nor the names of its
  16. // contributors may be used to endorse or promote products derived from
  17. // this software without specific prior written permission.
  18. //
  19. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #endregion
  31. using System;
  32. using System.IO;
  33. using System.Reflection;
  34. using System.Runtime.InteropServices;
  35. using System.Threading;
  36. using Grpc.Core.Logging;
  37. using Grpc.Core.Utils;
  38. namespace Grpc.Core.Internal
  39. {
  40. /// <summary>
  41. /// Represents a dynamically loaded unmanaged library in a (partially) platform independent manner.
  42. /// An important difference in library loading semantics is that on Windows, once we load a dynamic library using LoadLibrary,
  43. /// that library becomes instantly available for <c>DllImport</c> P/Invoke calls referring to the same library name.
  44. /// On Unix systems, dlopen has somewhat different semantics, so we need to use dlsym and <c>Marshal.GetDelegateForFunctionPointer</c>
  45. /// to obtain delegates to native methods.
  46. /// See http://stackoverflow.com/questions/13461989/p-invoke-to-dynamically-loaded-library-on-mono.
  47. /// </summary>
  48. internal class UnmanagedLibrary
  49. {
  50. static readonly ILogger Logger = GrpcEnvironment.Logger.ForType<UnmanagedLibrary>();
  51. // flags for dlopen
  52. const int RTLD_LAZY = 1;
  53. const int RTLD_GLOBAL = 8;
  54. readonly string libraryPath;
  55. readonly IntPtr handle;
  56. public UnmanagedLibrary(string[] libraryPathAlternatives)
  57. {
  58. this.libraryPath = FirstValidLibraryPath(libraryPathAlternatives);
  59. Logger.Debug("Attempting to load native library \"{0}\"", this.libraryPath);
  60. this.handle = PlatformSpecificLoadLibrary(this.libraryPath);
  61. if (this.handle == IntPtr.Zero)
  62. {
  63. throw new IOException(string.Format("Error loading native library \"{0}\"", this.libraryPath));
  64. }
  65. }
  66. /// <summary>
  67. /// Loads symbol in a platform specific way.
  68. /// </summary>
  69. /// <param name="symbolName"></param>
  70. /// <returns></returns>
  71. public IntPtr LoadSymbol(string symbolName)
  72. {
  73. if (PlatformApis.IsLinux)
  74. {
  75. if (PlatformApis.IsMono)
  76. {
  77. return Mono.dlsym(this.handle, symbolName);
  78. }
  79. return Linux.dlsym(this.handle, symbolName);
  80. }
  81. if (PlatformApis.IsMacOSX)
  82. {
  83. return MacOSX.dlsym(this.handle, symbolName);
  84. }
  85. throw new InvalidOperationException("Unsupported platform.");
  86. }
  87. public T GetNativeMethodDelegate<T>(string methodName)
  88. where T : class
  89. {
  90. var ptr = LoadSymbol(methodName);
  91. if (ptr == IntPtr.Zero)
  92. {
  93. throw new MissingMethodException(string.Format("The native method \"{0}\" does not exist", methodName));
  94. }
  95. return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T;
  96. }
  97. /// <summary>
  98. /// Loads library in a platform specific way.
  99. /// </summary>
  100. private static IntPtr PlatformSpecificLoadLibrary(string libraryPath)
  101. {
  102. if (PlatformApis.IsWindows)
  103. {
  104. return Windows.LoadLibrary(libraryPath);
  105. }
  106. if (PlatformApis.IsLinux)
  107. {
  108. if (PlatformApis.IsMono)
  109. {
  110. return Mono.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
  111. }
  112. return Linux.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
  113. }
  114. if (PlatformApis.IsMacOSX)
  115. {
  116. return MacOSX.dlopen(libraryPath, RTLD_GLOBAL + RTLD_LAZY);
  117. }
  118. throw new InvalidOperationException("Unsupported platform.");
  119. }
  120. private static string FirstValidLibraryPath(string[] libraryPathAlternatives)
  121. {
  122. GrpcPreconditions.CheckArgument(libraryPathAlternatives.Length > 0, "libraryPathAlternatives cannot be empty.");
  123. foreach (var path in libraryPathAlternatives)
  124. {
  125. if (File.Exists(path))
  126. {
  127. return path;
  128. }
  129. }
  130. throw new FileNotFoundException(String.Format("Error loading native library. Not found in any of the possible locations {0}", libraryPathAlternatives));
  131. }
  132. private static class Windows
  133. {
  134. [DllImport("kernel32.dll")]
  135. internal static extern IntPtr LoadLibrary(string filename);
  136. }
  137. private static class Linux
  138. {
  139. [DllImport("libdl.so")]
  140. internal static extern IntPtr dlopen(string filename, int flags);
  141. [DllImport("libdl.so")]
  142. internal static extern IntPtr dlsym(IntPtr handle, string symbol);
  143. }
  144. private static class MacOSX
  145. {
  146. [DllImport("libSystem.dylib")]
  147. internal static extern IntPtr dlopen(string filename, int flags);
  148. [DllImport("libSystem.dylib")]
  149. internal static extern IntPtr dlsym(IntPtr handle, string symbol);
  150. }
  151. /// <summary>
  152. /// On Linux systems, using using dlopen and dlsym results in
  153. /// DllNotFoundException("libdl.so not found") if libc6-dev
  154. /// is not installed. As a workaround, we load symbols for
  155. /// dlopen and dlsym from the current process as on Linux
  156. /// Mono sure is linked against these symbols.
  157. /// </summary>
  158. private static class Mono
  159. {
  160. [DllImport("__Internal")]
  161. internal static extern IntPtr dlopen(string filename, int flags);
  162. [DllImport("__Internal")]
  163. internal static extern IntPtr dlsym(IntPtr handle, string symbol);
  164. }
  165. }
  166. }