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