SahilPatidar cc8f7cd252
[ORC][LibraryResolver] Fix ensureFilterBuilt assertion failure and concurrency issue. (#166510)
- Fixed architecture compatibility check.
Previously, we used `sys::getDefaultTriple()`, which caused issues on
build bots
using cross-compilation. We now ensure that the target architecture
where the
shared library (.so) is run or loaded matches the architecture it was
built for.

- Fixed ensureFilterBuilt assertion failure.

- Replaced use of FilteredView with a safer alternative for concurrent
environments.
The old FilteredView approach iterated over shared state without
sufficient
synchronization, which could lead to invalid accesses when libraries
were being
added or removed concurrently.
2025-11-06 12:30:22 +05:30

368 lines
11 KiB
C++

//===- LibraryResolver.cpp - Library Resolution of Unresolved Symbols ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Library resolution impl for unresolved symbols
//
//===----------------------------------------------------------------------===//
#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryResolver.h"
#include "llvm/ExecutionEngine/Orc/TargetProcess/LibraryScanner.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ELF.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Error.h"
#include <mutex>
#include <thread>
#define DEBUG_TYPE "orc-resolver"
namespace llvm::orc {
LibraryResolver::LibraryResolver(const LibraryResolver::Setup &S)
: LibPathCache(S.Cache ? S.Cache : std::make_shared<LibraryPathCache>()),
LibPathResolver(S.PResolver
? S.PResolver
: std::make_shared<PathResolver>(LibPathCache)),
ScanHelper(S.BasePaths, LibPathCache, LibPathResolver),
FB(S.FilterBuilder), LibMgr(),
ShouldScanCall(S.ShouldScanCall ? S.ShouldScanCall
: [](StringRef) -> bool { return true; }),
scanBatchSize(S.ScanBatchSize) {
if (ScanHelper.getAllUnits().empty()) {
LLVM_DEBUG(dbgs() << "Warning: No base paths provided for scanning.\n");
}
}
std::unique_ptr<LibraryResolutionDriver>
LibraryResolutionDriver::create(const LibraryResolver::Setup &S) {
auto LR = std::make_unique<LibraryResolver>(S);
return std::unique_ptr<LibraryResolutionDriver>(
new LibraryResolutionDriver(std::move(LR)));
}
void LibraryResolutionDriver::addScanPath(const std::string &Path, PathType K) {
LR->ScanHelper.addBasePath(Path, K);
}
bool LibraryResolutionDriver::markLibraryLoaded(StringRef Path) {
auto Lib = LR->LibMgr.getLibrary(Path);
if (!Lib)
return false;
Lib->setState(LibraryManager::LibState::Loaded);
return true;
}
bool LibraryResolutionDriver::markLibraryUnLoaded(StringRef Path) {
auto Lib = LR->LibMgr.getLibrary(Path);
if (!Lib)
return false;
Lib->setState(LibraryManager::LibState::Unloaded);
return true;
}
void LibraryResolutionDriver::resolveSymbols(
std::vector<std::string> Syms,
LibraryResolver::OnSearchComplete OnCompletion,
const SearchConfig &Config) {
LR->searchSymbolsInLibraries(Syms, std::move(OnCompletion), Config);
}
static bool shouldIgnoreSymbol(const object::SymbolRef &Sym,
uint32_t IgnoreFlags) {
Expected<uint32_t> FlagsOrErr = Sym.getFlags();
if (!FlagsOrErr) {
consumeError(FlagsOrErr.takeError());
return true;
}
uint32_t Flags = *FlagsOrErr;
using Filter = SymbolEnumeratorOptions;
if ((IgnoreFlags & Filter::IgnoreUndefined) &&
(Flags & object::SymbolRef::SF_Undefined))
return true;
if ((IgnoreFlags & Filter::IgnoreIndirect) &&
(Flags & object::SymbolRef::SF_Indirect))
return true;
if ((IgnoreFlags & Filter::IgnoreWeak) &&
(Flags & object::SymbolRef::SF_Weak))
return true;
return false;
}
bool SymbolEnumerator::enumerateSymbols(StringRef Path, OnEachSymbolFn OnEach,
const SymbolEnumeratorOptions &Opts) {
if (Path.empty())
return false;
ObjectFileLoader ObjLoader(Path);
auto ObjOrErr = ObjLoader.getObjectFile();
if (!ObjOrErr) {
std::string ErrMsg;
handleAllErrors(ObjOrErr.takeError(),
[&](const ErrorInfoBase &EIB) { ErrMsg = EIB.message(); });
LLVM_DEBUG(dbgs() << "Failed loading object file: " << Path
<< "\nError: " << ErrMsg << "\n");
return false;
}
object::ObjectFile *Obj = &ObjOrErr.get();
auto processSymbolRange =
[&](object::ObjectFile::symbol_iterator_range Range) -> EnumerateResult {
for (const auto &Sym : Range) {
if (shouldIgnoreSymbol(Sym, Opts.FilterFlags))
continue;
auto NameOrErr = Sym.getName();
if (!NameOrErr) {
consumeError(NameOrErr.takeError());
continue;
}
StringRef Name = *NameOrErr;
if (Name.empty())
continue;
EnumerateResult Res = OnEach(Name);
if (Res != EnumerateResult::Continue)
return Res;
}
return EnumerateResult::Continue;
};
EnumerateResult Res = processSymbolRange(Obj->symbols());
if (Res != EnumerateResult::Continue)
return Res == EnumerateResult::Stop;
if (Obj->isELF()) {
const auto *ElfObj = cast<object::ELFObjectFileBase>(Obj);
Res = processSymbolRange(ElfObj->getDynamicSymbolIterators());
if (Res != EnumerateResult::Continue)
return Res == EnumerateResult::Stop;
} else if (Obj->isCOFF()) {
const auto *CoffObj = cast<object::COFFObjectFile>(Obj);
for (auto I = CoffObj->export_directory_begin(),
E = CoffObj->export_directory_end();
I != E; ++I) {
StringRef Name;
if (I->getSymbolName(Name))
continue;
if (Name.empty())
continue;
EnumerateResult Res = OnEach(Name);
if (Res != EnumerateResult::Continue)
return Res == EnumerateResult::Stop;
}
} else if (Obj->isMachO()) {
}
return true;
}
class SymbolSearchContext {
public:
SymbolSearchContext(SymbolQuery &Q) : Q(Q) {}
bool hasSearched(const LibraryInfo *Lib) const { return Searched.count(Lib); }
void markSearched(const LibraryInfo *Lib) { Searched.insert(Lib); }
inline bool allResolved() const { return Q.allResolved(); }
SymbolQuery &query() { return Q; }
private:
SymbolQuery &Q;
DenseSet<const LibraryInfo *> Searched;
};
void LibraryResolver::resolveSymbolsInLibrary(
LibraryInfo &Lib, SymbolQuery &UnresolvedSymbols,
const SymbolEnumeratorOptions &Opts) {
LLVM_DEBUG(dbgs() << "Checking unresolved symbols "
<< " in library : " << Lib.getFileName() << "\n";);
StringSet<> DiscoveredSymbols;
if (!UnresolvedSymbols.hasUnresolved()) {
LLVM_DEBUG(dbgs() << "Skipping library: " << Lib.getFullPath()
<< " — unresolved symbols exist.\n";);
return;
}
bool HasEnumerated = false;
auto enumerateSymbolsIfNeeded = [&]() {
if (HasEnumerated)
return;
HasEnumerated = true;
LLVM_DEBUG(dbgs() << "Enumerating symbols in library: " << Lib.getFullPath()
<< "\n";);
SymbolEnumerator::enumerateSymbols(
Lib.getFullPath(),
[&](StringRef sym) {
DiscoveredSymbols.insert(sym);
return EnumerateResult::Continue;
},
Opts);
};
if (!Lib.hasFilter()) {
LLVM_DEBUG(dbgs() << "Building filter for library: " << Lib.getFullPath()
<< "\n";);
enumerateSymbolsIfNeeded();
if (DiscoveredSymbols.empty()) {
LLVM_DEBUG(dbgs() << " No symbols and remove library : "
<< Lib.getFullPath() << "\n";);
LibMgr.removeLibrary(Lib.getFullPath());
return;
}
SmallVector<StringRef> SymbolVec;
SymbolVec.reserve(DiscoveredSymbols.size());
for (const auto &KV : DiscoveredSymbols)
SymbolVec.push_back(KV.first());
Lib.ensureFilterBuilt(FB, SymbolVec);
LLVM_DEBUG({
dbgs() << "DiscoveredSymbols : " << DiscoveredSymbols.size() << "\n";
for (const auto &KV : DiscoveredSymbols)
dbgs() << "DiscoveredSymbols : " << KV.first() << "\n";
});
}
const auto &Unresolved = UnresolvedSymbols.getUnresolvedSymbols();
bool HadAnySym = false;
LLVM_DEBUG(dbgs() << "Total unresolved symbols : " << Unresolved.size()
<< "\n";);
for (const auto &Sym : Unresolved) {
if (Lib.mayContain(Sym)) {
LLVM_DEBUG(dbgs() << "Checking symbol '" << Sym
<< "' in library: " << Lib.getFullPath() << "\n";);
enumerateSymbolsIfNeeded();
if (DiscoveredSymbols.count(Sym) > 0) {
LLVM_DEBUG(dbgs() << " Resolved symbol: " << Sym
<< " in library: " << Lib.getFullPath() << "\n";);
UnresolvedSymbols.resolve(Sym, Lib.getFullPath());
HadAnySym = true;
}
}
}
using LibraryState = LibraryManager::LibState;
if (HadAnySym && Lib.getState() != LibraryState::Loaded)
Lib.setState(LibraryState::Queried);
}
void LibraryResolver::searchSymbolsInLibraries(
std::vector<std::string> &SymbolList, OnSearchComplete OnComplete,
const SearchConfig &Config) {
SymbolQuery Q(SymbolList);
using LibraryState = LibraryManager::LibState;
using LibraryType = PathType;
auto tryResolveFrom = [&](LibraryState S, LibraryType K) {
LLVM_DEBUG(dbgs() << "Trying resolve from state=" << static_cast<int>(S)
<< " type=" << static_cast<int>(K) << "\n";);
SymbolSearchContext Ctx(Q);
while (!Ctx.allResolved()) {
std::vector<std::shared_ptr<LibraryInfo>> Libs;
LibMgr.getLibraries(S, K, Libs, [&](const LibraryInfo &Lib) {
return !Ctx.hasSearched(&Lib);
});
if (Libs.empty() && !scanLibrariesIfNeeded(K, scanBatchSize))
break; // no more new libs to scan
for (auto &Lib : Libs) {
// can use Async here?
resolveSymbolsInLibrary(*Lib, Ctx.query(), Config.Options);
Ctx.markSearched(Lib.get());
if (Ctx.allResolved())
return;
}
}
};
for (const auto &[St, Ty] : Config.Policy.Plan) {
tryResolveFrom(St, Ty);
if (Q.allResolved())
break;
}
// done:
LLVM_DEBUG({
dbgs() << "Search complete.\n";
for (const auto &r : Q.getAllResults())
dbgs() << "Resolved Symbol:" << r->Name << " -> " << r->ResolvedLibPath
<< "\n";
});
OnComplete(Q);
}
bool LibraryResolver::scanLibrariesIfNeeded(PathType PK, size_t BatchSize) {
LLVM_DEBUG(dbgs() << "LibraryResolver::scanLibrariesIfNeeded: Scanning for "
<< (PK == PathType::User ? "User" : "System")
<< " libraries\n";);
if (!ScanHelper.leftToScan(PK))
return false;
LibraryScanner Scanner(ScanHelper, LibMgr, ShouldScanCall);
Scanner.scanNext(PK, BatchSize);
return true;
}
bool LibraryResolver::symbolExistsInLibrary(const LibraryInfo &Lib,
StringRef SymName,
std::vector<std::string> *AllSyms) {
SymbolEnumeratorOptions Opts;
return symbolExistsInLibrary(Lib, SymName, AllSyms, Opts);
}
bool LibraryResolver::symbolExistsInLibrary(
const LibraryInfo &Lib, StringRef SymName,
std::vector<std::string> *AllSyms, const SymbolEnumeratorOptions &Opts) {
bool Found = false;
SymbolEnumerator::enumerateSymbols(
Lib.getFullPath(),
[&](StringRef Sym) {
if (AllSyms)
AllSyms->emplace_back(Sym.str());
if (Sym == SymName) {
Found = true;
}
return EnumerateResult::Continue;
},
Opts);
return Found;
}
} // end namespace llvm::orc