368 lines · cpp
1//===- LibraryResolver.cpp - Library Resolution of Unresolved Symbols ---===//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-exception6//7//===----------------------------------------------------------------------===//8//9// Library resolution impl for unresolved symbols10//11//===----------------------------------------------------------------------===//12 13#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h"14#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"15 16#include "llvm/ADT/StringSet.h"17 18#include "llvm/BinaryFormat/MachO.h"19#include "llvm/Object/COFF.h"20#include "llvm/Object/ELF.h"21#include "llvm/Object/ELFObjectFile.h"22#include "llvm/Object/MachO.h"23#include "llvm/Object/ObjectFile.h"24#include "llvm/Support/Error.h"25 26#include <mutex>27#include <thread>28 29#define DEBUG_TYPE "orc-resolver"30 31namespace llvm::orc {32 33LibraryResolver::LibraryResolver(const LibraryResolver::Setup &S)34 : LibPathCache(S.Cache ? S.Cache : std::make_shared<LibraryPathCache>()),35 LibPathResolver(S.PResolver36 ? S.PResolver37 : std::make_shared<PathResolver>(LibPathCache)),38 ScanHelper(S.BasePaths, LibPathCache, LibPathResolver),39 FB(S.FilterBuilder), LibMgr(),40 ShouldScanCall(S.ShouldScanCall ? S.ShouldScanCall41 : [](StringRef) -> bool { return true; }),42 scanBatchSize(S.ScanBatchSize) {43 44 if (ScanHelper.getAllUnits().empty()) {45 LLVM_DEBUG(dbgs() << "Warning: No base paths provided for scanning.\n");46 }47}48 49std::unique_ptr<LibraryResolutionDriver>50LibraryResolutionDriver::create(const LibraryResolver::Setup &S) {51 auto LR = std::make_unique<LibraryResolver>(S);52 return std::unique_ptr<LibraryResolutionDriver>(53 new LibraryResolutionDriver(std::move(LR)));54}55 56void LibraryResolutionDriver::addScanPath(const std::string &Path, PathType K) {57 LR->ScanHelper.addBasePath(Path, K);58}59 60bool LibraryResolutionDriver::markLibraryLoaded(StringRef Path) {61 auto Lib = LR->LibMgr.getLibrary(Path);62 if (!Lib)63 return false;64 65 Lib->setState(LibraryManager::LibState::Loaded);66 67 return true;68}69 70bool LibraryResolutionDriver::markLibraryUnLoaded(StringRef Path) {71 auto Lib = LR->LibMgr.getLibrary(Path);72 if (!Lib)73 return false;74 75 Lib->setState(LibraryManager::LibState::Unloaded);76 77 return true;78}79 80void LibraryResolutionDriver::resolveSymbols(81 std::vector<std::string> Syms,82 LibraryResolver::OnSearchComplete OnCompletion,83 const SearchConfig &Config) {84 LR->searchSymbolsInLibraries(Syms, std::move(OnCompletion), Config);85}86 87static bool shouldIgnoreSymbol(const object::SymbolRef &Sym,88 uint32_t IgnoreFlags) {89 Expected<uint32_t> FlagsOrErr = Sym.getFlags();90 if (!FlagsOrErr) {91 consumeError(FlagsOrErr.takeError());92 return true;93 }94 95 uint32_t Flags = *FlagsOrErr;96 97 using Filter = SymbolEnumeratorOptions;98 if ((IgnoreFlags & Filter::IgnoreUndefined) &&99 (Flags & object::SymbolRef::SF_Undefined))100 return true;101 if ((IgnoreFlags & Filter::IgnoreIndirect) &&102 (Flags & object::SymbolRef::SF_Indirect))103 return true;104 if ((IgnoreFlags & Filter::IgnoreWeak) &&105 (Flags & object::SymbolRef::SF_Weak))106 return true;107 108 return false;109}110 111bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,112 const SymbolEnumeratorOptions &Opts) {113 if (Path.empty())114 return false;115 116 ObjectFileLoader ObjLoader(Path);117 118 auto ObjOrErr = ObjLoader.getObjectFile();119 if (!ObjOrErr) {120 std::string ErrMsg;121 handleAllErrors(ObjOrErr.takeError(),122 [&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); });123 LLVM_DEBUG(dbgs() << "Failed loading object file: " << Path124 << "\nError: " << ErrMsg << "\n");125 return false;126 }127 128 object::ObjectFile *Obj = &ObjOrErr.get();129 130 auto processSymbolRange =131 [&](object::ObjectFile::symbol_iterator_range Range) -> EnumerateResult {132 for (const auto &Sym : Range) {133 if (shouldIgnoreSymbol(Sym, Opts.FilterFlags))134 continue;135 136 auto NameOrErr = Sym.getName();137 if (!NameOrErr) {138 consumeError(NameOrErr.takeError());139 continue;140 }141 142 StringRef Name = *NameOrErr;143 if (Name.empty())144 continue;145 146 EnumerateResult Res = OnEach(Name);147 if (Res != EnumerateResult::Continue)148 return Res;149 }150 return EnumerateResult::Continue;151 };152 153 EnumerateResult Res = processSymbolRange(Obj->symbols());154 if (Res != EnumerateResult::Continue)155 return Res == EnumerateResult::Stop;156 157 if (Obj->isELF()) {158 const auto *ElfObj = cast<object::ELFObjectFileBase>(Obj);159 Res = processSymbolRange(ElfObj->getDynamicSymbolIterators());160 if (Res != EnumerateResult::Continue)161 return Res == EnumerateResult::Stop;162 } else if (Obj->isCOFF()) {163 const auto *CoffObj = cast<object::COFFObjectFile>(Obj);164 for (auto I = CoffObj->export_directory_begin(),165 E = CoffObj->export_directory_end();166 I != E; ++I) {167 StringRef Name;168 if (I->getSymbolName(Name))169 continue;170 if (Name.empty())171 continue;172 173 EnumerateResult Res = OnEach(Name);174 if (Res != EnumerateResult::Continue)175 return Res == EnumerateResult::Stop;176 }177 } else if (Obj->isMachO()) {178 }179 180 return true;181}182 183class SymbolSearchContext {184public:185 SymbolSearchContext(SymbolQuery &Q) : Q(Q) {}186 187 bool hasSearched(const LibraryInfo *Lib) const { return Searched.count(Lib); }188 189 void markSearched(const LibraryInfo *Lib) { Searched.insert(Lib); }190 191 inline bool allResolved() const { return Q.allResolved(); }192 193 SymbolQuery &query() { return Q; }194 195private:196 SymbolQuery &Q;197 DenseSet<const LibraryInfo *> Searched;198};199 200void LibraryResolver::resolveSymbolsInLibrary(201 LibraryInfo &Lib, SymbolQuery &UnresolvedSymbols,202 const SymbolEnumeratorOptions &Opts) {203 LLVM_DEBUG(dbgs() << "Checking unresolved symbols "204 << " in library : " << Lib.getFileName() << "\n";);205 StringSet<> DiscoveredSymbols;206 207 if (!UnresolvedSymbols.hasUnresolved()) {208 LLVM_DEBUG(dbgs() << "Skipping library: " << Lib.getFullPath()209 << " — unresolved symbols exist.\n";);210 return;211 }212 213 bool HasEnumerated = false;214 auto enumerateSymbolsIfNeeded = [&]() {215 if (HasEnumerated)216 return;217 218 HasEnumerated = true;219 220 LLVM_DEBUG(dbgs() << "Enumerating symbols in library: " << Lib.getFullPath()221 << "\n";);222 SymbolEnumerator::enumerateSymbols(223 Lib.getFullPath(),224 [&](StringRef sym) {225 DiscoveredSymbols.insert(sym);226 return EnumerateResult::Continue;227 },228 Opts);229 };230 231 if (!Lib.hasFilter()) {232 LLVM_DEBUG(dbgs() << "Building filter for library: " << Lib.getFullPath()233 << "\n";);234 enumerateSymbolsIfNeeded();235 if (DiscoveredSymbols.empty()) {236 LLVM_DEBUG(dbgs() << " No symbols and remove library : "237 << Lib.getFullPath() << "\n";);238 LibMgr.removeLibrary(Lib.getFullPath());239 return;240 }241 SmallVector<StringRef> SymbolVec;242 SymbolVec.reserve(DiscoveredSymbols.size());243 for (const auto &KV : DiscoveredSymbols)244 SymbolVec.push_back(KV.first());245 246 Lib.ensureFilterBuilt(FB, SymbolVec);247 LLVM_DEBUG({248 dbgs() << "DiscoveredSymbols : " << DiscoveredSymbols.size() << "\n";249 for (const auto &KV : DiscoveredSymbols)250 dbgs() << "DiscoveredSymbols : " << KV.first() << "\n";251 });252 }253 254 const auto &Unresolved = UnresolvedSymbols.getUnresolvedSymbols();255 bool HadAnySym = false;256 LLVM_DEBUG(dbgs() << "Total unresolved symbols : " << Unresolved.size()257 << "\n";);258 for (const auto &Sym : Unresolved) {259 if (Lib.mayContain(Sym)) {260 LLVM_DEBUG(dbgs() << "Checking symbol '" << Sym261 << "' in library: " << Lib.getFullPath() << "\n";);262 enumerateSymbolsIfNeeded();263 if (DiscoveredSymbols.count(Sym) > 0) {264 LLVM_DEBUG(dbgs() << " Resolved symbol: " << Sym265 << " in library: " << Lib.getFullPath() << "\n";);266 UnresolvedSymbols.resolve(Sym, Lib.getFullPath());267 HadAnySym = true;268 }269 }270 }271 272 using LibraryState = LibraryManager::LibState;273 if (HadAnySym && Lib.getState() != LibraryState::Loaded)274 Lib.setState(LibraryState::Queried);275}276 277void LibraryResolver::searchSymbolsInLibraries(278 std::vector<std::string> &SymbolList, OnSearchComplete OnComplete,279 const SearchConfig &Config) {280 SymbolQuery Q(SymbolList);281 282 using LibraryState = LibraryManager::LibState;283 using LibraryType = PathType;284 auto tryResolveFrom = [&](LibraryState S, LibraryType K) {285 LLVM_DEBUG(dbgs() << "Trying resolve from state=" << static_cast<int>(S)286 << " type=" << static_cast<int>(K) << "\n";);287 288 SymbolSearchContext Ctx(Q);289 while (!Ctx.allResolved()) {290 std::vector<std::shared_ptr<LibraryInfo>> Libs;291 LibMgr.getLibraries(S, K, Libs, [&](const LibraryInfo &Lib) {292 return !Ctx.hasSearched(&Lib);293 });294 295 if (Libs.empty() && !scanLibrariesIfNeeded(K, scanBatchSize))296 break; // no more new libs to scan297 298 for (auto &Lib : Libs) {299 // can use Async here?300 resolveSymbolsInLibrary(*Lib, Ctx.query(), Config.Options);301 Ctx.markSearched(Lib.get());302 303 if (Ctx.allResolved())304 return;305 }306 }307 };308 309 for (const auto &[St, Ty] : Config.Policy.Plan) {310 tryResolveFrom(St, Ty);311 if (Q.allResolved())312 break;313 }314 315 // done:316 LLVM_DEBUG({317 dbgs() << "Search complete.\n";318 for (const auto &r : Q.getAllResults())319 dbgs() << "Resolved Symbol:" << r->Name << " -> " << r->ResolvedLibPath320 << "\n";321 });322 323 OnComplete(Q);324}325 326bool LibraryResolver::scanLibrariesIfNeeded(PathType PK, size_t BatchSize) {327 LLVM_DEBUG(dbgs() << "LibraryResolver::scanLibrariesIfNeeded: Scanning for "328 << (PK == PathType::User ? "User" : "System")329 << " libraries\n";);330 if (!ScanHelper.leftToScan(PK))331 return false;332 333 LibraryScanner Scanner(ScanHelper, LibMgr, ShouldScanCall);334 Scanner.scanNext(PK, BatchSize);335 return true;336}337 338bool LibraryResolver::symbolExistsInLibrary(const LibraryInfo &Lib,339 StringRef SymName,340 std::vector<std::string> *AllSyms) {341 SymbolEnumeratorOptions Opts;342 return symbolExistsInLibrary(Lib, SymName, AllSyms, Opts);343}344 345bool LibraryResolver::symbolExistsInLibrary(346 const LibraryInfo &Lib, StringRef SymName,347 std::vector<std::string> *AllSyms, const SymbolEnumeratorOptions &Opts) {348 bool Found = false;349 350 SymbolEnumerator::enumerateSymbols(351 Lib.getFullPath(),352 [&](StringRef Sym) {353 if (AllSyms)354 AllSyms->emplace_back(Sym.str());355 356 if (Sym == SymName) {357 Found = true;358 }359 360 return EnumerateResult::Continue;361 },362 Opts);363 364 return Found;365}366 367} // end namespace llvm::orc368