1//===-- DynamicLibrary.cpp - Runtime link/load libraries --------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  This file implements the operating system DynamicLibrary concept.
10//
11//===----------------------------------------------------------------------===//
12
13#include "llvm/Support/DynamicLibrary.h"
14#include "llvm-c/Support.h"
15#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringMap.h"
17#include "llvm/Config/config.h"
18#include "llvm/Support/Mutex.h"
19#include <vector>
20
21using namespace llvm;
22using namespace llvm::sys;
23
24// All methods for HandleSet should be used holding SymbolsMutex.
25class DynamicLibrary::HandleSet {
26  typedef std::vector<void *> HandleList;
27  HandleList Handles;
28  void *Process = nullptr;
29
30public:
31  static void *DLOpen(const char *Filename, std::string *Err);
32  static void DLClose(void *Handle);
33  static void *DLSym(void *Handle, const char *Symbol);
34
35  HandleSet() = default;
36  ~HandleSet();
37
38  HandleList::iterator Find(void *Handle) { return find(Handles, Handle); }
39
40  bool Contains(void *Handle) {
41    return Handle == Process || Find(Handle) != Handles.end();
42  }
43
44  bool AddLibrary(void *Handle, bool IsProcess = false, bool CanClose = true,
45                  bool AllowDuplicates = false) {
46#ifdef _WIN32
47    assert((Handle == this ? IsProcess : !IsProcess) && "Bad Handle.");
48#endif
49    assert((!AllowDuplicates || !CanClose) &&
50           "CanClose must be false if AllowDuplicates is true.");
51
52    if (LLVM_LIKELY(!IsProcess)) {
53      if (!AllowDuplicates && Find(Handle) != Handles.end()) {
54        if (CanClose)
55          DLClose(Handle);
56        return false;
57      }
58      Handles.push_back(Handle);
59    } else {
60#ifndef _WIN32
61      if (Process) {
62        if (CanClose)
63          DLClose(Process);
64        if (Process == Handle)
65          return false;
66      }
67#endif
68      Process = Handle;
69    }
70    return true;
71  }
72
73  void CloseLibrary(void *Handle) {
74    DLClose(Handle);
75    HandleList::iterator it = Find(Handle);
76    if (it != Handles.end()) {
77      Handles.erase(it);
78    }
79  }
80
81  void *LibLookup(const char *Symbol, DynamicLibrary::SearchOrdering Order) {
82    if (Order & SO_LoadOrder) {
83      for (void *Handle : Handles) {
84        if (void *Ptr = DLSym(Handle, Symbol))
85          return Ptr;
86      }
87    } else {
88      for (void *Handle : llvm::reverse(Handles)) {
89        if (void *Ptr = DLSym(Handle, Symbol))
90          return Ptr;
91      }
92    }
93    return nullptr;
94  }
95
96  void *Lookup(const char *Symbol, DynamicLibrary::SearchOrdering Order) {
97    assert(!((Order & SO_LoadedFirst) && (Order & SO_LoadedLast)) &&
98           "Invalid Ordering");
99
100    if (!Process || (Order & SO_LoadedFirst)) {
101      if (void *Ptr = LibLookup(Symbol, Order))
102        return Ptr;
103    }
104    if (Process) {
105      // Use OS facilities to search the current binary and all loaded libs.
106      if (void *Ptr = DLSym(Process, Symbol))
107        return Ptr;
108
109      // Search any libs that might have been skipped because of RTLD_LOCAL.
110      if (Order & SO_LoadedLast) {
111        if (void *Ptr = LibLookup(Symbol, Order))
112          return Ptr;
113      }
114    }
115    return nullptr;
116  }
117};
118
119namespace {
120
121struct Globals {
122  // Collection of symbol name/value pairs to be searched prior to any
123  // libraries.
124  llvm::StringMap<void *> ExplicitSymbols;
125  // Collections of known library handles.
126  DynamicLibrary::HandleSet OpenedHandles;
127  DynamicLibrary::HandleSet OpenedTemporaryHandles;
128  // Lock for ExplicitSymbols, OpenedHandles, and OpenedTemporaryHandles.
129  llvm::sys::SmartMutex<true> SymbolsMutex;
130};
131
132Globals &getGlobals() {
133  static Globals G;
134  return G;
135}
136
137} // namespace
138
139#ifdef _WIN32
140
141#include "Windows/DynamicLibrary.inc"
142
143#else
144
145#include "Unix/DynamicLibrary.inc"
146
147#endif
148
149char DynamicLibrary::Invalid;
150DynamicLibrary::SearchOrdering DynamicLibrary::SearchOrder =
151    DynamicLibrary::SO_Linker;
152
153namespace llvm {
154void *SearchForAddressOfSpecialSymbol(const char *SymbolName) {
155  return DoSearch(SymbolName); // DynamicLibrary.inc
156}
157} // namespace llvm
158
159void DynamicLibrary::AddSymbol(StringRef SymbolName, void *SymbolValue) {
160  auto &G = getGlobals();
161  SmartScopedLock<true> Lock(G.SymbolsMutex);
162  G.ExplicitSymbols[SymbolName] = SymbolValue;
163}
164
165DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *FileName,
166                                                   std::string *Err) {
167  auto &G = getGlobals();
168  void *Handle = HandleSet::DLOpen(FileName, Err);
169  if (Handle != &Invalid) {
170    SmartScopedLock<true> Lock(G.SymbolsMutex);
171    G.OpenedHandles.AddLibrary(Handle, /*IsProcess*/ FileName == nullptr);
172  }
173
174  return DynamicLibrary(Handle);
175}
176
177DynamicLibrary DynamicLibrary::addPermanentLibrary(void *Handle,
178                                                   std::string *Err) {
179  auto &G = getGlobals();
180  SmartScopedLock<true> Lock(G.SymbolsMutex);
181  // If we've already loaded this library, tell the caller.
182  if (!G.OpenedHandles.AddLibrary(Handle, /*IsProcess*/ false,
183                                  /*CanClose*/ false))
184    *Err = "Library already loaded";
185
186  return DynamicLibrary(Handle);
187}
188
189DynamicLibrary DynamicLibrary::getLibrary(const char *FileName,
190                                          std::string *Err) {
191  assert(FileName && "Use getPermanentLibrary() for opening process handle");
192  void *Handle = HandleSet::DLOpen(FileName, Err);
193  if (Handle != &Invalid) {
194    auto &G = getGlobals();
195    SmartScopedLock<true> Lock(G.SymbolsMutex);
196    G.OpenedTemporaryHandles.AddLibrary(Handle, /*IsProcess*/ false,
197                                        /*CanClose*/ false,
198                                        /*AllowDuplicates*/ true);
199  }
200  return DynamicLibrary(Handle);
201}
202
203void DynamicLibrary::closeLibrary(DynamicLibrary &Lib) {
204  auto &G = getGlobals();
205  SmartScopedLock<true> Lock(G.SymbolsMutex);
206  if (Lib.isValid()) {
207    G.OpenedTemporaryHandles.CloseLibrary(Lib.Data);
208    Lib.Data = &Invalid;
209  }
210}
211
212void *DynamicLibrary::getAddressOfSymbol(const char *SymbolName) {
213  if (!isValid())
214    return nullptr;
215  return HandleSet::DLSym(Data, SymbolName);
216}
217
218void *DynamicLibrary::SearchForAddressOfSymbol(const char *SymbolName) {
219  {
220    auto &G = getGlobals();
221    SmartScopedLock<true> Lock(G.SymbolsMutex);
222
223    // First check symbols added via AddSymbol().
224    StringMap<void *>::iterator i = G.ExplicitSymbols.find(SymbolName);
225
226    if (i != G.ExplicitSymbols.end())
227      return i->second;
228
229    // Now search the libraries.
230    if (void *Ptr = G.OpenedHandles.Lookup(SymbolName, SearchOrder))
231      return Ptr;
232    if (void *Ptr = G.OpenedTemporaryHandles.Lookup(SymbolName, SearchOrder))
233      return Ptr;
234  }
235
236  return llvm::SearchForAddressOfSpecialSymbol(SymbolName);
237}
238
239//===----------------------------------------------------------------------===//
240// C API.
241//===----------------------------------------------------------------------===//
242
243LLVMBool LLVMLoadLibraryPermanently(const char *Filename) {
244  return llvm::sys::DynamicLibrary::LoadLibraryPermanently(Filename);
245}
246
247void *LLVMSearchForAddressOfSymbol(const char *symbolName) {
248  return llvm::sys::DynamicLibrary::SearchForAddressOfSymbol(symbolName);
249}
250
251void LLVMAddSymbol(const char *symbolName, void *symbolValue) {
252  return llvm::sys::DynamicLibrary::AddSymbol(symbolName, symbolValue);
253}
254