1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Runtime.InteropServices; 10 using System.Linq; 11 using ELFSharp; 12 using System.Collections.Generic; 13 using ELFSharp.ELF; 14 using ELFSharp.ELF.Sections; 15 using System.IO; 16 using System.Text; 17 using System.ComponentModel; 18 19 namespace Antmicro.Renode.Utilities 20 { 21 public static class SharedLibraries 22 { 23 /// <summary> 24 /// Loads the given library to memory. 25 /// </summary> 26 /// <returns> 27 /// The address of the loaded library. 28 /// </returns> 29 /// <param name='path'> 30 /// Path to the library file. 31 /// </param> 32 /// <param name='relocation'> 33 /// Whether relocation should be done immediately after loading or being deferred (lazy). 34 /// The default option is to relocate immediately. 35 /// </param> LoadLibrary(string path)36 public static IntPtr LoadLibrary(string path) 37 { 38 IntPtr address; 39 if (!TryLoadLibrary(path, out address)) 40 { 41 HandleError("opening"); 42 } 43 return address; 44 } 45 TryLoadLibrary(string path, out IntPtr address)46 public static bool TryLoadLibrary(string path, out IntPtr address) 47 { 48 #if PLATFORM_WINDOWS 49 address = WindowsLoadLibrary(path); 50 #else 51 //HACK: returns 0 on first call, somehow 52 dlerror(); 53 54 int dlopenFlags = RTLD_NOW; // relocation now 55 56 #if PLATFORM_OSX 57 // RTLD_LOCAL prevents loaded symbols from being "made available to resolve references 58 // in subsequently loaded shared objects.". It's the default on Linux. 59 // 60 // With the alternative RTLD_GLOBAL flag, which is the default on macOS, tlib's weak 61 // arch-independent functions that are implemented only for some architectures will be 62 // resolved by implementations from previously loaded tlib for other architectures. 63 // This is certainly an unwanted behavior so we need to pass RTLD_LOCAL on macOS. 64 dlopenFlags |= RTLD_LOCAL; 65 #endif 66 67 address = dlopen(path, dlopenFlags); 68 #endif 69 return address != IntPtr.Zero; 70 } 71 72 /// <summary> 73 /// Unloads the library and frees memory taken by it. 74 /// </summary> 75 /// <param name='address'> 76 /// Address of the library, returned by the <see cref="LoadLibrary" /> function. 77 /// </param> UnloadLibrary(IntPtr address)78 public static void UnloadLibrary(IntPtr address) 79 { 80 #if PLATFORM_WINDOWS 81 var result = WindowsCloseLibrary(address); 82 if (!result) 83 { 84 HandleError("unloading"); 85 } 86 #else 87 var result = dlclose(address); 88 if (result != 0) 89 { 90 HandleError("unloading"); 91 } 92 #endif 93 } 94 95 /// <summary> 96 /// Gets all exported symbol names for a given library. 97 /// </summary> 98 /// <returns> 99 /// Exported symbol names. 100 /// </returns> 101 /// <param name='path'> 102 /// Path to a library file. 103 /// </param> 104 /// <remarks> 105 /// Currently it works only with ELF files. 106 /// </remarks> GetAllSymbols(string path)107 public static IEnumerable<string> GetAllSymbols(string path) 108 { 109 ELFSharp.MachO.MachO machO; 110 if(ELFSharp.MachO.MachOReader.TryLoad(path, out machO) == ELFSharp.MachO.MachOResult.OK) 111 { 112 var machoSymtab = machO.GetCommandsOfType<ELFSharp.MachO.SymbolTable>().Single(); 113 // it can happen that binary contain multiple entries for a single symbol name, 114 // so we should filter it out here 115 return machoSymtab.Symbols.Select(x => x.Name.TrimStart('_')).Distinct(); 116 } 117 ELFSharp.PE.PE pe; 118 if(ELFSharp.PE.PEReader.TryLoad(path, out pe)) 119 { 120 return pe.GetExportedSymbols(); 121 } 122 using(var elf = ELFUtils.LoadELF(path)) 123 { 124 var symtab = (ISymbolTable)elf.GetSection(".symtab"); 125 return symtab.Entries.Select(x => x.Name); 126 } 127 } 128 129 /// <summary> 130 /// Gets the address of the symbol with a given name. 131 /// </summary> 132 /// <returns> 133 /// The address of the symbol in memory. 134 /// </returns> 135 /// <param name='libraryAddress'> 136 /// Address to library returned by the <see cref="LoadLibrary" /> function. 137 /// </param> 138 /// <param name='name'> 139 /// Name of the symbol to retrieve. 140 /// </param> GetSymbolAddress(IntPtr libraryAddress, string name)141 public static IntPtr GetSymbolAddress(IntPtr libraryAddress, string name) 142 { 143 #if PLATFORM_WINDOWS 144 var address = WindowsGetSymbolAddress(libraryAddress, name); 145 #else 146 var address = dlsym(libraryAddress, name); 147 #endif 148 if (address == IntPtr.Zero) 149 { 150 HandleError("getting symbol from"); 151 } 152 return address; 153 } 154 HandleError(string operation)155 private static void HandleError(string operation) 156 { 157 string message = null; 158 #if PLATFORM_WINDOWS 159 var errno = Marshal.GetLastWin32Error(); 160 //For an unknown reason, in some cases, Windows doesn't set error code. 161 if(errno != 0) 162 { 163 message = new Win32Exception(errno).Message; 164 } 165 #else 166 var messagePtr = dlerror(); 167 if(messagePtr != IntPtr.Zero) 168 { 169 message = Marshal.PtrToStringAuto(messagePtr); 170 } 171 #endif 172 throw new InvalidOperationException(string.Format("Error while {1} dynamic library: {0}", message ?? "unknown error", operation)); 173 } 174 175 #if PLATFORM_WINDOWS 176 [DllImport("kernel32", SetLastError=true, CharSet = CharSet.Ansi, EntryPoint="LoadLibrary")] WindowsLoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName)177 static extern IntPtr WindowsLoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); 178 179 [DllImport("kernel32.dll", EntryPoint="GetProcAddress")] WindowsGetSymbolAddress(IntPtr hModule, string symbolName)180 public static extern IntPtr WindowsGetSymbolAddress(IntPtr hModule, string symbolName); 181 182 [DllImport("kernel32.dll", EntryPoint="FreeLibrary")] WindowsCloseLibrary(IntPtr hModule)183 public static extern bool WindowsCloseLibrary(IntPtr hModule); 184 185 [DllImport("kernel32.dll", EntryPoint="GetLastError")] WindowsGetLastError()186 public static extern UInt32 WindowsGetLastError(); 187 #elif PLATFORM_LINUX 188 [DllImport("libdl.so.2")] dlopen(string file, int mode)189 private static extern IntPtr dlopen(string file, int mode); 190 191 [DllImport("libdl.so.2")] dlerror()192 private static extern IntPtr dlerror(); 193 194 [DllImport("libdl.so.2")] dlsym(IntPtr handle, string name)195 private static extern IntPtr dlsym(IntPtr handle, string name); 196 197 [DllImport("libdl.so.2")] dlclose(IntPtr handle)198 private static extern int dlclose(IntPtr handle); 199 200 // Source: https://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h;hb=HEAD 201 private const int RTLD_NOW = 2; 202 #else 203 [DllImport("dl")] dlopen(string file, int mode)204 private static extern IntPtr dlopen(string file, int mode); 205 206 [DllImport("dl")] dlerror()207 private static extern IntPtr dlerror(); 208 209 [DllImport("dl")] dlsym(IntPtr handle, string name)210 private static extern IntPtr dlsym(IntPtr handle, string name); 211 212 [DllImport("dl")] dlclose(IntPtr handle)213 private static extern int dlclose(IntPtr handle); 214 215 // Source: https://opensource.apple.com/source/dyld/dyld-239.3/include/dlfcn.h.auto.html 216 private const int RTLD_NOW = 2; 217 private const int RTLD_LOCAL = 4; 218 #endif 219 } 220 } 221 222