//
// Copyright (c) 2010-2024 Antmicro
// Copyright (c) 2011-2015 Realtime Embedded
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//
using System;
using System.Runtime.InteropServices;
using System.Linq;
using ELFSharp;
using System.Collections.Generic;
using ELFSharp.ELF;
using ELFSharp.ELF.Sections;
using System.IO;
using System.Text;
using System.ComponentModel;
namespace Antmicro.Renode.Utilities
{
public static class SharedLibraries
{
///
/// Loads the given library to memory.
///
///
/// The address of the loaded library.
///
///
/// Path to the library file.
///
///
/// Whether relocation should be done immediately after loading or being deferred (lazy).
/// The default option is to relocate immediately.
///
public static IntPtr LoadLibrary(string path)
{
IntPtr address;
if (!TryLoadLibrary(path, out address))
{
HandleError("opening");
}
return address;
}
public static bool TryLoadLibrary(string path, out IntPtr address)
{
#if PLATFORM_WINDOWS
address = WindowsLoadLibrary(path);
#else
//HACK: returns 0 on first call, somehow
dlerror();
int dlopenFlags = RTLD_NOW; // relocation now
#if PLATFORM_OSX
// RTLD_LOCAL prevents loaded symbols from being "made available to resolve references
// in subsequently loaded shared objects.". It's the default on Linux.
//
// With the alternative RTLD_GLOBAL flag, which is the default on macOS, tlib's weak
// arch-independent functions that are implemented only for some architectures will be
// resolved by implementations from previously loaded tlib for other architectures.
// This is certainly an unwanted behavior so we need to pass RTLD_LOCAL on macOS.
dlopenFlags |= RTLD_LOCAL;
#endif
address = dlopen(path, dlopenFlags);
#endif
return address != IntPtr.Zero;
}
///
/// Unloads the library and frees memory taken by it.
///
///
/// Address of the library, returned by the function.
///
public static void UnloadLibrary(IntPtr address)
{
#if PLATFORM_WINDOWS
var result = WindowsCloseLibrary(address);
if (!result)
{
HandleError("unloading");
}
#else
var result = dlclose(address);
if (result != 0)
{
HandleError("unloading");
}
#endif
}
///
/// Gets all exported symbol names for a given library.
///
///
/// Exported symbol names.
///
///
/// Path to a library file.
///
///
/// Currently it works only with ELF files.
///
public static IEnumerable GetAllSymbols(string path)
{
ELFSharp.MachO.MachO machO;
if(ELFSharp.MachO.MachOReader.TryLoad(path, out machO) == ELFSharp.MachO.MachOResult.OK)
{
var machoSymtab = machO.GetCommandsOfType().Single();
// it can happen that binary contain multiple entries for a single symbol name,
// so we should filter it out here
return machoSymtab.Symbols.Select(x => x.Name.TrimStart('_')).Distinct();
}
ELFSharp.PE.PE pe;
if(ELFSharp.PE.PEReader.TryLoad(path, out pe))
{
return pe.GetExportedSymbols();
}
using(var elf = ELFUtils.LoadELF(path))
{
var symtab = (ISymbolTable)elf.GetSection(".symtab");
return symtab.Entries.Select(x => x.Name);
}
}
///
/// Gets the address of the symbol with a given name.
///
///
/// The address of the symbol in memory.
///
///
/// Address to library returned by the function.
///
///
/// Name of the symbol to retrieve.
///
public static IntPtr GetSymbolAddress(IntPtr libraryAddress, string name)
{
#if PLATFORM_WINDOWS
var address = WindowsGetSymbolAddress(libraryAddress, name);
#else
var address = dlsym(libraryAddress, name);
#endif
if (address == IntPtr.Zero)
{
HandleError("getting symbol from");
}
return address;
}
private static void HandleError(string operation)
{
string message = null;
#if PLATFORM_WINDOWS
var errno = Marshal.GetLastWin32Error();
//For an unknown reason, in some cases, Windows doesn't set error code.
if(errno != 0)
{
message = new Win32Exception(errno).Message;
}
#else
var messagePtr = dlerror();
if(messagePtr != IntPtr.Zero)
{
message = Marshal.PtrToStringAuto(messagePtr);
}
#endif
throw new InvalidOperationException(string.Format("Error while {1} dynamic library: {0}", message ?? "unknown error", operation));
}
#if PLATFORM_WINDOWS
[DllImport("kernel32", SetLastError=true, CharSet = CharSet.Ansi, EntryPoint="LoadLibrary")]
static extern IntPtr WindowsLoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32.dll", EntryPoint="GetProcAddress")]
public static extern IntPtr WindowsGetSymbolAddress(IntPtr hModule, string symbolName);
[DllImport("kernel32.dll", EntryPoint="FreeLibrary")]
public static extern bool WindowsCloseLibrary(IntPtr hModule);
[DllImport("kernel32.dll", EntryPoint="GetLastError")]
public static extern UInt32 WindowsGetLastError();
#elif PLATFORM_LINUX
[DllImport("libdl.so.2")]
private static extern IntPtr dlopen(string file, int mode);
[DllImport("libdl.so.2")]
private static extern IntPtr dlerror();
[DllImport("libdl.so.2")]
private static extern IntPtr dlsym(IntPtr handle, string name);
[DllImport("libdl.so.2")]
private static extern int dlclose(IntPtr handle);
// Source: https://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h;hb=HEAD
private const int RTLD_NOW = 2;
#else
[DllImport("dl")]
private static extern IntPtr dlopen(string file, int mode);
[DllImport("dl")]
private static extern IntPtr dlerror();
[DllImport("dl")]
private static extern IntPtr dlsym(IntPtr handle, string name);
[DllImport("dl")]
private static extern int dlclose(IntPtr handle);
// Source: https://opensource.apple.com/source/dyld/dyld-239.3/include/dlfcn.h.auto.html
private const int RTLD_NOW = 2;
private const int RTLD_LOCAL = 4;
#endif
}
}