HarfbuzzWorkaround.cs 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. using System.Runtime.InteropServices;
  2. namespace XEmbedSample;
  3. /*
  4. This is needed specifically for GtkSharp:
  5. https://github.com/mono/SkiaSharp/issues/3038
  6. https://github.com/GtkSharp/GtkSharp/issues/443
  7. Instead of using plain DllImport they are manually calling dlopen with RTLD_GLOBAL and RTLD_LAZY flags:
  8. https://github.com/GtkSharp/GtkSharp/blob/b7303616129ab5a0ca64def45649ab522d83fa4a/Source/Libs/Shared/FuncLoader.cs#L80-L92
  9. Which causes libHarfBuzzSharp.so from HarfBuzzSharp to resolve some of the symbols from the system libharfbuzz.so.0
  10. which is a _different_ harfbuzz version.
  11. That results in a segfault.
  12. Previously there was a workaround - https://github.com/mono/SkiaSharp/pull/2247 but it got
  13. disabled for .NET Core / .NET 5+.
  14. Why linux linker builds shared libraries in a way that makes it possible for them to resolve their own symbols from
  15. elsewhere escapes me.
  16. Here we are loading libHarfBuzzSharp.so from the .NET-resolved location, saving it, unloading the library
  17. and then defining a custom resolver that would call dlopen with RTLD_NOW + RTLD_DEEPBIND
  18. */
  19. public unsafe class HarfbuzzWorkaround
  20. {
  21. [DllImport("libc")]
  22. static extern int dlinfo(IntPtr handle, int request, IntPtr info);
  23. [DllImport("libc")]
  24. static extern IntPtr dlopen(string filename, int flags);
  25. private const int RTLD_DI_ORIGIN = 6;
  26. private const int RTLD_NOW = 2;
  27. private const int RTLD_DEEPBIND = 8;
  28. public static void Apply()
  29. {
  30. if (RuntimeInformation.RuntimeIdentifier.Contains("musl"))
  31. throw new PlatformNotSupportedException("musl doesn't support RTLD_DEEPBIND");
  32. var libraryPathBytes = Marshal.AllocHGlobal(4096);
  33. var handle = NativeLibrary.Load("libHarfBuzzSharp", typeof(HarfBuzzSharp.Blob).Assembly, null);
  34. dlinfo(handle, RTLD_DI_ORIGIN, libraryPathBytes);
  35. var libraryOrigin = Marshal.PtrToStringUTF8(libraryPathBytes) ?? string.Empty;
  36. Marshal.FreeHGlobal(libraryPathBytes);
  37. var libraryPath = Path.Combine(libraryOrigin, "libHarfBuzzSharp.so");
  38. NativeLibrary.Free(handle);
  39. var forceLoadedHandle = dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
  40. if (forceLoadedHandle == IntPtr.Zero)
  41. throw new DllNotFoundException($"Unable to load {libraryPath} via dlopen");
  42. NativeLibrary.SetDllImportResolver(typeof(HarfBuzzSharp.Blob).Assembly, (name, assembly, searchPath) =>
  43. {
  44. if (name.Contains("HarfBuzzSharp"))
  45. return dlopen(libraryPath, RTLD_NOW | RTLD_DEEPBIND);
  46. return NativeLibrary.Load(name, assembly, searchPath);
  47. });
  48. }
  49. }