1 //===- JITSymbol.h - JIT symbol abstraction ---------------------*- 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 // Abstraction for target process addresses. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_EXECUTIONENGINE_JITSYMBOL_H 14 #define LLVM_EXECUTIONENGINE_JITSYMBOL_H 15 16 #include <algorithm> 17 #include <cassert> 18 #include <cstddef> 19 #include <cstdint> 20 #include <functional> 21 #include <map> 22 #include <set> 23 #include <string> 24 25 #include "llvm/ADT/BitmaskEnum.h" 26 #include "llvm/ADT/FunctionExtras.h" 27 #include "llvm/ADT/StringRef.h" 28 #include "llvm/Support/Error.h" 29 30 namespace llvm { 31 32 class GlobalValue; 33 class GlobalValueSummary; 34 35 namespace object { 36 37 class SymbolRef; 38 39 } // end namespace object 40 41 /// Represents an address in the target process's address space. 42 using JITTargetAddress = uint64_t; 43 44 /// Convert a JITTargetAddress to a pointer. 45 /// 46 /// Note: This is a raw cast of the address bit pattern to the given pointer 47 /// type. When casting to a function pointer in order to execute JIT'd code 48 /// jitTargetAddressToFunction should be preferred, as it will also perform 49 /// pointer signing on targets that require it. 50 template <typename T> T jitTargetAddressToPointer(JITTargetAddress Addr) { 51 static_assert(std::is_pointer<T>::value, "T must be a pointer type"); 52 uintptr_t IntPtr = static_cast<uintptr_t>(Addr); 53 assert(IntPtr == Addr && "JITTargetAddress value out of range for uintptr_t"); 54 return reinterpret_cast<T>(IntPtr); 55 } 56 57 /// Convert a JITTargetAddress to a callable function pointer. 58 /// 59 /// Casts the given address to a callable function pointer. This operation 60 /// will perform pointer signing for platforms that require it (e.g. arm64e). 61 template <typename T> T jitTargetAddressToFunction(JITTargetAddress Addr) { 62 static_assert(std::is_pointer<T>::value && 63 std::is_function<std::remove_pointer_t<T>>::value, 64 "T must be a function pointer type"); 65 return jitTargetAddressToPointer<T>(Addr); 66 } 67 68 /// Convert a pointer to a JITTargetAddress. 69 template <typename T> JITTargetAddress pointerToJITTargetAddress(T *Ptr) { 70 return static_cast<JITTargetAddress>(reinterpret_cast<uintptr_t>(Ptr)); 71 } 72 73 /// Flags for symbols in the JIT. 74 class JITSymbolFlags { 75 public: 76 using UnderlyingType = uint8_t; 77 using TargetFlagsType = uint8_t; 78 79 enum FlagNames : UnderlyingType { 80 None = 0, 81 HasError = 1U << 0, 82 Weak = 1U << 1, 83 Common = 1U << 2, 84 Absolute = 1U << 3, 85 Exported = 1U << 4, 86 Callable = 1U << 5, 87 MaterializationSideEffectsOnly = 1U << 6, 88 LLVM_MARK_AS_BITMASK_ENUM( // LargestValue = 89 MaterializationSideEffectsOnly) 90 }; 91 92 /// Default-construct a JITSymbolFlags instance. 93 JITSymbolFlags() = default; 94 95 /// Construct a JITSymbolFlags instance from the given flags. 96 JITSymbolFlags(FlagNames Flags) : Flags(Flags) {} 97 98 /// Construct a JITSymbolFlags instance from the given flags and target 99 /// flags. 100 JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags) 101 : TargetFlags(TargetFlags), Flags(Flags) {} 102 103 /// Implicitly convert to bool. Returns true if any flag is set. 104 explicit operator bool() const { return Flags != None || TargetFlags != 0; } 105 106 /// Compare for equality. 107 bool operator==(const JITSymbolFlags &RHS) const { 108 return Flags == RHS.Flags && TargetFlags == RHS.TargetFlags; 109 } 110 111 /// Bitwise AND-assignment for FlagNames. 112 JITSymbolFlags &operator&=(const FlagNames &RHS) { 113 Flags &= RHS; 114 return *this; 115 } 116 117 /// Bitwise OR-assignment for FlagNames. 118 JITSymbolFlags &operator|=(const FlagNames &RHS) { 119 Flags |= RHS; 120 return *this; 121 } 122 123 /// Return true if there was an error retrieving this symbol. 124 bool hasError() const { 125 return (Flags & HasError) == HasError; 126 } 127 128 /// Returns true if the Weak flag is set. 129 bool isWeak() const { 130 return (Flags & Weak) == Weak; 131 } 132 133 /// Returns true if the Common flag is set. 134 bool isCommon() const { 135 return (Flags & Common) == Common; 136 } 137 138 /// Returns true if the symbol isn't weak or common. 139 bool isStrong() const { 140 return !isWeak() && !isCommon(); 141 } 142 143 /// Returns true if the Exported flag is set. 144 bool isExported() const { 145 return (Flags & Exported) == Exported; 146 } 147 148 /// Returns true if the given symbol is known to be callable. 149 bool isCallable() const { return (Flags & Callable) == Callable; } 150 151 /// Returns true if this symbol is a materialization-side-effects-only 152 /// symbol. Such symbols do not have a real address. They exist to trigger 153 /// and support synchronization of materialization side effects, e.g. for 154 /// collecting initialization information. These symbols will vanish from 155 /// the symbol table immediately upon reaching the ready state, and will 156 /// appear to queries as if they were never defined (except that query 157 /// callback execution will be delayed until they reach the ready state). 158 /// MaterializationSideEffectOnly symbols should only be queried using the 159 /// SymbolLookupFlags::WeaklyReferencedSymbol flag (see 160 /// llvm/include/llvm/ExecutionEngine/Orc/Core.h). 161 bool hasMaterializationSideEffectsOnly() const { 162 return (Flags & MaterializationSideEffectsOnly) == 163 MaterializationSideEffectsOnly; 164 } 165 166 /// Get the underlying flags value as an integer. 167 UnderlyingType getRawFlagsValue() const { 168 return static_cast<UnderlyingType>(Flags); 169 } 170 171 /// Return a reference to the target-specific flags. 172 TargetFlagsType& getTargetFlags() { return TargetFlags; } 173 174 /// Return a reference to the target-specific flags. 175 const TargetFlagsType& getTargetFlags() const { return TargetFlags; } 176 177 /// Construct a JITSymbolFlags value based on the flags of the given global 178 /// value. 179 static JITSymbolFlags fromGlobalValue(const GlobalValue &GV); 180 181 /// Construct a JITSymbolFlags value based on the flags of the given global 182 /// value summary. 183 static JITSymbolFlags fromSummary(GlobalValueSummary *S); 184 185 /// Construct a JITSymbolFlags value based on the flags of the given libobject 186 /// symbol. 187 static Expected<JITSymbolFlags> 188 fromObjectSymbol(const object::SymbolRef &Symbol); 189 190 private: 191 TargetFlagsType TargetFlags = 0; 192 FlagNames Flags = None; 193 }; 194 195 inline JITSymbolFlags operator&(const JITSymbolFlags &LHS, 196 const JITSymbolFlags::FlagNames &RHS) { 197 JITSymbolFlags Tmp = LHS; 198 Tmp &= RHS; 199 return Tmp; 200 } 201 202 inline JITSymbolFlags operator|(const JITSymbolFlags &LHS, 203 const JITSymbolFlags::FlagNames &RHS) { 204 JITSymbolFlags Tmp = LHS; 205 Tmp |= RHS; 206 return Tmp; 207 } 208 209 /// ARM-specific JIT symbol flags. 210 /// FIXME: This should be moved into a target-specific header. 211 class ARMJITSymbolFlags { 212 public: 213 ARMJITSymbolFlags() = default; 214 215 enum FlagNames { 216 None = 0, 217 Thumb = 1 << 0 218 }; 219 220 operator JITSymbolFlags::TargetFlagsType&() { return Flags; } 221 222 static ARMJITSymbolFlags fromObjectSymbol(const object::SymbolRef &Symbol); 223 224 private: 225 JITSymbolFlags::TargetFlagsType Flags = 0; 226 }; 227 228 /// Represents a symbol that has been evaluated to an address already. 229 class JITEvaluatedSymbol { 230 public: 231 JITEvaluatedSymbol() = default; 232 233 /// Create a 'null' symbol. 234 JITEvaluatedSymbol(std::nullptr_t) {} 235 236 /// Create a symbol for the given address and flags. 237 JITEvaluatedSymbol(JITTargetAddress Address, JITSymbolFlags Flags) 238 : Address(Address), Flags(Flags) {} 239 240 /// Create a symbol from the given pointer with the given flags. 241 template <typename T> 242 static JITEvaluatedSymbol 243 fromPointer(T *P, JITSymbolFlags Flags = JITSymbolFlags::Exported) { 244 return JITEvaluatedSymbol(pointerToJITTargetAddress(P), Flags); 245 } 246 247 /// An evaluated symbol converts to 'true' if its address is non-zero. 248 explicit operator bool() const { return Address != 0; } 249 250 /// Return the address of this symbol. 251 JITTargetAddress getAddress() const { return Address; } 252 253 /// Return the flags for this symbol. 254 JITSymbolFlags getFlags() const { return Flags; } 255 256 /// Set the flags for this symbol. 257 void setFlags(JITSymbolFlags Flags) { this->Flags = std::move(Flags); } 258 259 private: 260 JITTargetAddress Address = 0; 261 JITSymbolFlags Flags; 262 }; 263 264 /// Represents a symbol in the JIT. 265 class JITSymbol { 266 public: 267 using GetAddressFtor = unique_function<Expected<JITTargetAddress>()>; 268 269 /// Create a 'null' symbol, used to represent a "symbol not found" 270 /// result from a successful (non-erroneous) lookup. 271 JITSymbol(std::nullptr_t) 272 : CachedAddr(0) {} 273 274 /// Create a JITSymbol representing an error in the symbol lookup 275 /// process (e.g. a network failure during a remote lookup). 276 JITSymbol(Error Err) 277 : Err(std::move(Err)), Flags(JITSymbolFlags::HasError) {} 278 279 /// Create a symbol for a definition with a known address. 280 JITSymbol(JITTargetAddress Addr, JITSymbolFlags Flags) 281 : CachedAddr(Addr), Flags(Flags) {} 282 283 /// Construct a JITSymbol from a JITEvaluatedSymbol. 284 JITSymbol(JITEvaluatedSymbol Sym) 285 : CachedAddr(Sym.getAddress()), Flags(Sym.getFlags()) {} 286 287 /// Create a symbol for a definition that doesn't have a known address 288 /// yet. 289 /// @param GetAddress A functor to materialize a definition (fixing the 290 /// address) on demand. 291 /// 292 /// This constructor allows a JIT layer to provide a reference to a symbol 293 /// definition without actually materializing the definition up front. The 294 /// user can materialize the definition at any time by calling the getAddress 295 /// method. 296 JITSymbol(GetAddressFtor GetAddress, JITSymbolFlags Flags) 297 : GetAddress(std::move(GetAddress)), CachedAddr(0), Flags(Flags) {} 298 299 JITSymbol(const JITSymbol&) = delete; 300 JITSymbol& operator=(const JITSymbol&) = delete; 301 302 JITSymbol(JITSymbol &&Other) 303 : GetAddress(std::move(Other.GetAddress)), Flags(std::move(Other.Flags)) { 304 if (Flags.hasError()) 305 Err = std::move(Other.Err); 306 else 307 CachedAddr = std::move(Other.CachedAddr); 308 } 309 310 JITSymbol& operator=(JITSymbol &&Other) { 311 GetAddress = std::move(Other.GetAddress); 312 Flags = std::move(Other.Flags); 313 if (Flags.hasError()) 314 Err = std::move(Other.Err); 315 else 316 CachedAddr = std::move(Other.CachedAddr); 317 return *this; 318 } 319 320 ~JITSymbol() { 321 if (Flags.hasError()) 322 Err.~Error(); 323 else 324 CachedAddr.~JITTargetAddress(); 325 } 326 327 /// Returns true if the symbol exists, false otherwise. 328 explicit operator bool() const { 329 return !Flags.hasError() && (CachedAddr || GetAddress); 330 } 331 332 /// Move the error field value out of this JITSymbol. 333 Error takeError() { 334 if (Flags.hasError()) 335 return std::move(Err); 336 return Error::success(); 337 } 338 339 /// Get the address of the symbol in the target address space. Returns 340 /// '0' if the symbol does not exist. 341 Expected<JITTargetAddress> getAddress() { 342 assert(!Flags.hasError() && "getAddress called on error value"); 343 if (GetAddress) { 344 if (auto CachedAddrOrErr = GetAddress()) { 345 GetAddress = nullptr; 346 CachedAddr = *CachedAddrOrErr; 347 assert(CachedAddr && "Symbol could not be materialized."); 348 } else 349 return CachedAddrOrErr.takeError(); 350 } 351 return CachedAddr; 352 } 353 354 JITSymbolFlags getFlags() const { return Flags; } 355 356 private: 357 GetAddressFtor GetAddress; 358 union { 359 JITTargetAddress CachedAddr; 360 Error Err; 361 }; 362 JITSymbolFlags Flags; 363 }; 364 365 /// Symbol resolution interface. 366 /// 367 /// Allows symbol flags and addresses to be looked up by name. 368 /// Symbol queries are done in bulk (i.e. you request resolution of a set of 369 /// symbols, rather than a single one) to reduce IPC overhead in the case of 370 /// remote JITing, and expose opportunities for parallel compilation. 371 class JITSymbolResolver { 372 public: 373 using LookupSet = std::set<StringRef>; 374 using LookupResult = std::map<StringRef, JITEvaluatedSymbol>; 375 using OnResolvedFunction = unique_function<void(Expected<LookupResult>)>; 376 377 virtual ~JITSymbolResolver() = default; 378 379 /// Returns the fully resolved address and flags for each of the given 380 /// symbols. 381 /// 382 /// This method will return an error if any of the given symbols can not be 383 /// resolved, or if the resolution process itself triggers an error. 384 virtual void lookup(const LookupSet &Symbols, 385 OnResolvedFunction OnResolved) = 0; 386 387 /// Returns the subset of the given symbols that should be materialized by 388 /// the caller. Only weak/common symbols should be looked up, as strong 389 /// definitions are implicitly always part of the caller's responsibility. 390 virtual Expected<LookupSet> 391 getResponsibilitySet(const LookupSet &Symbols) = 0; 392 393 /// Specify if this resolver can return valid symbols with zero value. 394 virtual bool allowsZeroSymbols() { return false; } 395 396 private: 397 virtual void anchor(); 398 }; 399 400 /// Legacy symbol resolution interface. 401 class LegacyJITSymbolResolver : public JITSymbolResolver { 402 public: 403 /// Performs lookup by, for each symbol, first calling 404 /// findSymbolInLogicalDylib and if that fails calling 405 /// findSymbol. 406 void lookup(const LookupSet &Symbols, OnResolvedFunction OnResolved) final; 407 408 /// Performs flags lookup by calling findSymbolInLogicalDylib and 409 /// returning the flags value for that symbol. 410 Expected<LookupSet> getResponsibilitySet(const LookupSet &Symbols) final; 411 412 /// This method returns the address of the specified symbol if it exists 413 /// within the logical dynamic library represented by this JITSymbolResolver. 414 /// Unlike findSymbol, queries through this interface should return addresses 415 /// for hidden symbols. 416 /// 417 /// This is of particular importance for the Orc JIT APIs, which support lazy 418 /// compilation by breaking up modules: Each of those broken out modules 419 /// must be able to resolve hidden symbols provided by the others. Clients 420 /// writing memory managers for MCJIT can usually ignore this method. 421 /// 422 /// This method will be queried by RuntimeDyld when checking for previous 423 /// definitions of common symbols. 424 virtual JITSymbol findSymbolInLogicalDylib(const std::string &Name) = 0; 425 426 /// This method returns the address of the specified function or variable. 427 /// It is used to resolve symbols during module linking. 428 /// 429 /// If the returned symbol's address is equal to ~0ULL then RuntimeDyld will 430 /// skip all relocations for that symbol, and the client will be responsible 431 /// for handling them manually. 432 virtual JITSymbol findSymbol(const std::string &Name) = 0; 433 434 private: 435 void anchor() override; 436 }; 437 438 } // end namespace llvm 439 440 #endif // LLVM_EXECUTIONENGINE_JITSYMBOL_H 441