using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Security.AccessControl; namespace BizHawk.Common { /// Implementors are able to provide pointers to functions in dynamically-linked libraries, which are loaded through some undefined mechanism. /// public interface IImportResolver { IntPtr? GetProcAddrOrNull(string entryPoint); /// could not find symbol IntPtr GetProcAddrOrThrow(string entryPoint); } public class DynamicLibraryImportResolver : IDisposable, IImportResolver { private static readonly Lazy> asdf = new Lazy>(() => { var currDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase).Replace("file:", ""); return new[] { "/usr/lib/", "/usr/lib/bizhawk/", "./", "./dll/" }.Select(dir => dir[0] == '.' ? currDir + dir.Substring(1) : dir); }); private IntPtr _p; public DynamicLibraryImportResolver(string dllName) { static string ResolveFilePath(string orig) => orig[0] == '/' ? orig : asdf.Value.Select(dir => dir + orig).FirstOrDefault(File.Exists) ?? orig; _p = OSTailoredCode.LinkedLibManager.LoadOrThrow(OSTailoredCode.IsUnixHost ? ResolveFilePath(dllName) : dllName); } public IntPtr? GetProcAddrOrNull(string entryPoint) => OSTailoredCode.LinkedLibManager.GetProcAddrOrNull(_p, entryPoint); public IntPtr GetProcAddrOrThrow(string entryPoint) => OSTailoredCode.LinkedLibManager.GetProcAddrOrThrow(_p, entryPoint); private void DisposeHelper() { if (_p == IntPtr.Zero) return; // already freed OSTailoredCode.LinkedLibManager.FreeByPtr(_p); _p = IntPtr.Zero; } public void Dispose() { DisposeHelper(); GC.SuppressFinalize(this); } ~DynamicLibraryImportResolver() { DisposeHelper(); } } public class InstanceDll : IDisposable, IImportResolver { public IntPtr HModule { get; private set; } public InstanceDll(string dllPath) { // copy the dll to a temp directory var path = TempFileManager.GetTempFilename(Path.GetFileNameWithoutExtension(dllPath), ".dll", false); using var stream = new FileStream(path, FileMode.Create, FileSystemRights.FullControl, FileShare.ReadWrite | FileShare.Delete, 4 * 1024, FileOptions.None); using var sdll = File.OpenRead(dllPath); sdll.CopyTo(stream); // try to locate dlls in the current directory (for libretro cores) // this isn't foolproof but it's a little better than nothing // setting PWD temporarily doesn't work. that'd be ideal since it supposedly gets searched early on, // but i guess not with SetDllDirectory in effect var envpath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); try { var envpath_new = $"{Path.GetDirectoryName(path)};{envpath}"; Environment.SetEnvironmentVariable("PATH", envpath_new, EnvironmentVariableTarget.Process); HModule = OSTailoredCode.LinkedLibManager.LoadOrThrow(path); // consider using LoadLibraryEx instead of shenanigans? var newfname = TempFileManager.RenameTempFilenameForDelete(path); File.Move(path, newfname); } catch { // ignored } Environment.SetEnvironmentVariable("PATH", envpath, EnvironmentVariableTarget.Process); } public IntPtr? GetProcAddrOrNull(string procName) => OSTailoredCode.LinkedLibManager.GetProcAddrOrNull(HModule, procName); public IntPtr GetProcAddrOrThrow(string procName) => OSTailoredCode.LinkedLibManager.GetProcAddrOrThrow(HModule, procName); public void Dispose() { if (HModule == IntPtr.Zero) return; // already freed OSTailoredCode.LinkedLibManager.FreeByPtr(HModule); HModule = IntPtr.Zero; } } /// Aggregates resolvers, resolving addresses by searching through them, starting with the last. public class PatchImportResolver : IImportResolver { private readonly List _resolvers; public PatchImportResolver(params IImportResolver[] resolvers) { _resolvers = resolvers.ToList(); } public IntPtr? GetProcAddrOrNull(string entryPoint) { for (var i = _resolvers.Count - 1; i != -1; i--) { var ret = _resolvers[i].GetProcAddrOrNull(entryPoint); if (ret != null) return ret.Value; } return null; } public IntPtr GetProcAddrOrThrow(string entryPoint) => GetProcAddrOrNull(entryPoint) ?? throw new InvalidOperationException($"{entryPoint} was not found in any of the aggregated resolvers"); } }