1 //===- JITSymbol.h - JIT symbol abstraction ---------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Abstraction for target process addresses. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_EXECUTIONENGINE_JITSYMBOL_H 15 #define LLVM_EXECUTIONENGINE_JITSYMBOL_H 16 17 #include <algorithm> 18 #include <cassert> 19 #include <cstddef> 20 #include <cstdint> 21 #include <functional> 22 #include <map> 23 #include <set> 24 #include <string> 25 26 #include "llvm/ADT/StringRef.h" 27 #include "llvm/Support/Error.h" 28 29 namespace llvm { 30 31 class GlobalValue; 32 33 namespace object { 34 35 class BasicSymbolRef; 36 37 } // end namespace object 38 39 /// Represents an address in the target process's address space. 40 using JITTargetAddress = uint64_t; 41 42 /// Flags for symbols in the JIT. 43 class JITSymbolFlags { 44 public: 45 using UnderlyingType = uint8_t; 46 using TargetFlagsType = uint64_t; 47 48 enum FlagNames : UnderlyingType { 49 None = 0, 50 HasError = 1U << 0, 51 Weak = 1U << 1, 52 Common = 1U << 2, 53 Absolute = 1U << 3, 54 Exported = 1U << 4, 55 Lazy = 1U << 5, 56 Materializing = 1U << 6 57 }; 58 stripTransientFlags(JITSymbolFlags Orig)59 static JITSymbolFlags stripTransientFlags(JITSymbolFlags Orig) { 60 return static_cast<FlagNames>(Orig.Flags & ~Lazy & ~Materializing); 61 } 62 63 /// Default-construct a JITSymbolFlags instance. 64 JITSymbolFlags() = default; 65 66 /// Construct a JITSymbolFlags instance from the given flags. JITSymbolFlags(FlagNames Flags)67 JITSymbolFlags(FlagNames Flags) : Flags(Flags) {} 68 69 /// Construct a JITSymbolFlags instance from the given flags and target 70 /// flags. JITSymbolFlags(FlagNames Flags,TargetFlagsType TargetFlags)71 JITSymbolFlags(FlagNames Flags, TargetFlagsType TargetFlags) 72 : Flags(Flags), TargetFlags(TargetFlags) {} 73 74 /// Return true if there was an error retrieving this symbol. hasError()75 bool hasError() const { 76 return (Flags & HasError) == HasError; 77 } 78 79 /// Returns true if this is a lazy symbol. 80 /// This flag is used internally by the JIT APIs to track 81 /// materialization states. isLazy()82 bool isLazy() const { return Flags & Lazy; } 83 84 /// Returns true if this symbol is in the process of being 85 /// materialized. isMaterializing()86 bool isMaterializing() const { return Flags & Materializing; } 87 88 /// Returns true if this symbol is fully materialized. 89 /// (i.e. neither lazy, nor materializing). isMaterialized()90 bool isMaterialized() const { return !(Flags & (Lazy | Materializing)); } 91 92 /// Returns true if the Weak flag is set. isWeak()93 bool isWeak() const { 94 return (Flags & Weak) == Weak; 95 } 96 97 /// Returns true if the Common flag is set. isCommon()98 bool isCommon() const { 99 return (Flags & Common) == Common; 100 } 101 102 /// Returns true if the symbol isn't weak or common. isStrong()103 bool isStrong() const { 104 return !isWeak() && !isCommon(); 105 } 106 107 /// Returns true if the Exported flag is set. isExported()108 bool isExported() const { 109 return (Flags & Exported) == Exported; 110 } 111 112 /// Implicitly convert to the underlying flags type. 113 operator UnderlyingType&() { return Flags; } 114 115 /// Implicitly convert to the underlying flags type. 116 operator const UnderlyingType&() const { return Flags; } 117 118 /// Return a reference to the target-specific flags. getTargetFlags()119 TargetFlagsType& getTargetFlags() { return TargetFlags; } 120 121 /// Return a reference to the target-specific flags. getTargetFlags()122 const TargetFlagsType& getTargetFlags() const { return TargetFlags; } 123 124 /// Construct a JITSymbolFlags value based on the flags of the given global 125 /// value. 126 static JITSymbolFlags fromGlobalValue(const GlobalValue &GV); 127 128 /// Construct a JITSymbolFlags value based on the flags of the given libobject 129 /// symbol. 130 static JITSymbolFlags fromObjectSymbol(const object::BasicSymbolRef &Symbol); 131 132 private: 133 UnderlyingType Flags = None; 134 TargetFlagsType TargetFlags = 0; 135 }; 136 137 /// ARM-specific JIT symbol flags. 138 /// FIXME: This should be moved into a target-specific header. 139 class ARMJITSymbolFlags { 140 public: 141 ARMJITSymbolFlags() = default; 142 143 enum FlagNames { 144 None = 0, 145 Thumb = 1 << 0 146 }; 147 148 operator JITSymbolFlags::TargetFlagsType&() { return Flags; } 149 150 static ARMJITSymbolFlags fromObjectSymbol( 151 const object::BasicSymbolRef &Symbol); 152 private: 153 JITSymbolFlags::TargetFlagsType Flags = 0; 154 }; 155 156 /// Represents a symbol that has been evaluated to an address already. 157 class JITEvaluatedSymbol { 158 public: 159 JITEvaluatedSymbol() = default; 160 161 /// Create a 'null' symbol. JITEvaluatedSymbol(std::nullptr_t)162 JITEvaluatedSymbol(std::nullptr_t) {} 163 164 /// Create a symbol for the given address and flags. JITEvaluatedSymbol(JITTargetAddress Address,JITSymbolFlags Flags)165 JITEvaluatedSymbol(JITTargetAddress Address, JITSymbolFlags Flags) 166 : Address(Address), Flags(Flags) {} 167 168 /// An evaluated symbol converts to 'true' if its address is non-zero. 169 explicit operator bool() const { return Address != 0; } 170 171 /// Return the address of this symbol. getAddress()172 JITTargetAddress getAddress() const { return Address; } 173 174 /// Return the flags for this symbol. getFlags()175 JITSymbolFlags getFlags() const { return Flags; } 176 177 /// Set the flags for this symbol. setFlags(JITSymbolFlags Flags)178 void setFlags(JITSymbolFlags Flags) { this->Flags = std::move(Flags); } 179 180 private: 181 JITTargetAddress Address = 0; 182 JITSymbolFlags Flags; 183 }; 184 185 /// Represents a symbol in the JIT. 186 class JITSymbol { 187 public: 188 using GetAddressFtor = std::function<Expected<JITTargetAddress>()>; 189 190 /// Create a 'null' symbol, used to represent a "symbol not found" 191 /// result from a successful (non-erroneous) lookup. JITSymbol(std::nullptr_t)192 JITSymbol(std::nullptr_t) 193 : CachedAddr(0) {} 194 195 /// Create a JITSymbol representing an error in the symbol lookup 196 /// process (e.g. a network failure during a remote lookup). JITSymbol(Error Err)197 JITSymbol(Error Err) 198 : Err(std::move(Err)), Flags(JITSymbolFlags::HasError) {} 199 200 /// Create a symbol for a definition with a known address. JITSymbol(JITTargetAddress Addr,JITSymbolFlags Flags)201 JITSymbol(JITTargetAddress Addr, JITSymbolFlags Flags) 202 : CachedAddr(Addr), Flags(Flags) {} 203 204 /// Construct a JITSymbol from a JITEvaluatedSymbol. JITSymbol(JITEvaluatedSymbol Sym)205 JITSymbol(JITEvaluatedSymbol Sym) 206 : CachedAddr(Sym.getAddress()), Flags(Sym.getFlags()) {} 207 208 /// Create a symbol for a definition that doesn't have a known address 209 /// yet. 210 /// @param GetAddress A functor to materialize a definition (fixing the 211 /// address) on demand. 212 /// 213 /// This constructor allows a JIT layer to provide a reference to a symbol 214 /// definition without actually materializing the definition up front. The 215 /// user can materialize the definition at any time by calling the getAddress 216 /// method. JITSymbol(GetAddressFtor GetAddress,JITSymbolFlags Flags)217 JITSymbol(GetAddressFtor GetAddress, JITSymbolFlags Flags) 218 : GetAddress(std::move(GetAddress)), CachedAddr(0), Flags(Flags) {} 219 220 JITSymbol(const JITSymbol&) = delete; 221 JITSymbol& operator=(const JITSymbol&) = delete; 222 JITSymbol(JITSymbol && Other)223 JITSymbol(JITSymbol &&Other) 224 : GetAddress(std::move(Other.GetAddress)), Flags(std::move(Other.Flags)) { 225 if (Flags.hasError()) 226 Err = std::move(Other.Err); 227 else 228 CachedAddr = std::move(Other.CachedAddr); 229 } 230 231 JITSymbol& operator=(JITSymbol &&Other) { 232 GetAddress = std::move(Other.GetAddress); 233 Flags = std::move(Other.Flags); 234 if (Flags.hasError()) 235 Err = std::move(Other.Err); 236 else 237 CachedAddr = std::move(Other.CachedAddr); 238 return *this; 239 } 240 ~JITSymbol()241 ~JITSymbol() { 242 if (Flags.hasError()) 243 Err.~Error(); 244 else 245 CachedAddr.~JITTargetAddress(); 246 } 247 248 /// Returns true if the symbol exists, false otherwise. 249 explicit operator bool() const { 250 return !Flags.hasError() && (CachedAddr || GetAddress); 251 } 252 253 /// Move the error field value out of this JITSymbol. takeError()254 Error takeError() { 255 if (Flags.hasError()) 256 return std::move(Err); 257 return Error::success(); 258 } 259 260 /// Get the address of the symbol in the target address space. Returns 261 /// '0' if the symbol does not exist. getAddress()262 Expected<JITTargetAddress> getAddress() { 263 assert(!Flags.hasError() && "getAddress called on error value"); 264 if (GetAddress) { 265 if (auto CachedAddrOrErr = GetAddress()) { 266 GetAddress = nullptr; 267 CachedAddr = *CachedAddrOrErr; 268 assert(CachedAddr && "Symbol could not be materialized."); 269 } else 270 return CachedAddrOrErr.takeError(); 271 } 272 return CachedAddr; 273 } 274 getFlags()275 JITSymbolFlags getFlags() const { return Flags; } 276 277 private: 278 GetAddressFtor GetAddress; 279 union { 280 JITTargetAddress CachedAddr; 281 Error Err; 282 }; 283 JITSymbolFlags Flags; 284 }; 285 286 /// Symbol resolution interface. 287 /// 288 /// Allows symbol flags and addresses to be looked up by name. 289 /// Symbol queries are done in bulk (i.e. you request resolution of a set of 290 /// symbols, rather than a single one) to reduce IPC overhead in the case of 291 /// remote JITing, and expose opportunities for parallel compilation. 292 class JITSymbolResolver { 293 public: 294 using LookupSet = std::set<StringRef>; 295 using LookupResult = std::map<StringRef, JITEvaluatedSymbol>; 296 using LookupFlagsResult = std::map<StringRef, JITSymbolFlags>; 297 298 virtual ~JITSymbolResolver() = default; 299 300 /// Returns the fully resolved address and flags for each of the given 301 /// symbols. 302 /// 303 /// This method will return an error if any of the given symbols can not be 304 /// resolved, or if the resolution process itself triggers an error. 305 virtual Expected<LookupResult> lookup(const LookupSet &Symbols) = 0; 306 307 /// Returns the symbol flags for each of the given symbols. 308 /// 309 /// This method does NOT return an error if any of the given symbols is 310 /// missing. Instead, that symbol will be left out of the result map. 311 virtual Expected<LookupFlagsResult> lookupFlags(const LookupSet &Symbols) = 0; 312 313 private: 314 virtual void anchor(); 315 }; 316 317 /// Legacy symbol resolution interface. 318 class LegacyJITSymbolResolver : public JITSymbolResolver { 319 public: 320 /// Performs lookup by, for each symbol, first calling 321 /// findSymbolInLogicalDylib and if that fails calling 322 /// findSymbol. 323 Expected<LookupResult> lookup(const LookupSet &Symbols) final; 324 325 /// Performs flags lookup by calling findSymbolInLogicalDylib and 326 /// returning the flags value for that symbol. 327 Expected<LookupFlagsResult> lookupFlags(const LookupSet &Symbols) final; 328 329 /// This method returns the address of the specified symbol if it exists 330 /// within the logical dynamic library represented by this JITSymbolResolver. 331 /// Unlike findSymbol, queries through this interface should return addresses 332 /// for hidden symbols. 333 /// 334 /// This is of particular importance for the Orc JIT APIs, which support lazy 335 /// compilation by breaking up modules: Each of those broken out modules 336 /// must be able to resolve hidden symbols provided by the others. Clients 337 /// writing memory managers for MCJIT can usually ignore this method. 338 /// 339 /// This method will be queried by RuntimeDyld when checking for previous 340 /// definitions of common symbols. 341 virtual JITSymbol findSymbolInLogicalDylib(const std::string &Name) = 0; 342 343 /// This method returns the address of the specified function or variable. 344 /// It is used to resolve symbols during module linking. 345 /// 346 /// If the returned symbol's address is equal to ~0ULL then RuntimeDyld will 347 /// skip all relocations for that symbol, and the client will be responsible 348 /// for handling them manually. 349 virtual JITSymbol findSymbol(const std::string &Name) = 0; 350 351 private: 352 virtual void anchor(); 353 }; 354 355 } // end namespace llvm 356 357 #endif // LLVM_EXECUTIONENGINE_JITSYMBOL_H 358