From b2097e838aee2205b110f3314283f563f5c4f146 Mon Sep 17 00:00:00 2001 From: Mark Harfouche Date: Tue, 26 May 2026 00:00:00 +0000 Subject: [PATCH 1/2] loader: optional build-time-configured driver search directory The Linux driver discovery (v1.28.x) gained a stat()-based pre-filter (getLibrarySearchPaths) that only checks LD_LIBRARY_PATH, the system locations (/lib, /usr/lib, multiarch), and /etc/ld.so.conf. A driver installed in a relocatable prefix that is not on any of those (e.g. a conda environment) is filtered out before the dlopen that would otherwise load it -- even though its soname is already in the default knownDriverNames list. Add an optional compile-time macro ZE_LOADER_EXTRA_DRIVER_DIR. When defined by the build system to a directory (a package can set it to its own install prefix's lib dir), that directory is appended to the search paths immediately after LD_LIBRARY_PATH -- so a user's LD_LIBRARY_PATH still wins -- and before the system locations. Undefined by default, so upstream/system builds are unchanged. CMake injects the macro as a quoted string literal via target_compile_definitions when ZE_LOADER_EXTRA_DRIVER_DIR is passed at configure time. --- source/loader/CMakeLists.txt | 10 ++++++++++ source/loader/linux/driver_discovery_lin.cpp | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/source/loader/CMakeLists.txt b/source/loader/CMakeLists.txt index cbebf449..b7943afc 100644 --- a/source/loader/CMakeLists.txt +++ b/source/loader/CMakeLists.txt @@ -38,3 +38,13 @@ else() ) endif() +# An optional build-time configured directory to search for drivers, in +# addition to LD_LIBRARY_PATH and the system locations. Packaging (e.g. +# conda-forge) sets this to the install prefix's lib dir so that a driver +# co-installed in the same prefix is discovered by default. CMake handles the +# string quoting for the compile definition. +if(DEFINED ZE_LOADER_EXTRA_DRIVER_DIR AND NOT ZE_LOADER_EXTRA_DRIVER_DIR STREQUAL "") + target_compile_definitions(${TARGET_LOADER_NAME} + PRIVATE ZE_LOADER_EXTRA_DRIVER_DIR="${ZE_LOADER_EXTRA_DRIVER_DIR}") +endif() + diff --git a/source/loader/linux/driver_discovery_lin.cpp b/source/loader/linux/driver_discovery_lin.cpp index f349b7e9..a200a14d 100644 --- a/source/loader/linux/driver_discovery_lin.cpp +++ b/source/loader/linux/driver_discovery_lin.cpp @@ -46,6 +46,15 @@ static std::vector getLibrarySearchPaths() { auto split = splitPaths(ldLibPath); paths.insert(paths.end(), split.begin(), split.end()); } +#ifdef ZE_LOADER_EXTRA_DRIVER_DIR + // A driver directory configured at build time (e.g. the install prefix's lib + // dir, baked in at compile time and relocated on install). Searched right + // after LD_LIBRARY_PATH -- so a user's LD_LIBRARY_PATH still takes precedence + // -- but before the system locations. This lets a driver co-installed in the + // same prefix (e.g. a conda environment) be discovered by default, without + // having to set ZE_ENABLE_ALT_DRIVERS. + paths.push_back(ZE_LOADER_EXTRA_DRIVER_DIR); +#endif #if defined(ANDROID) paths.push_back("/vendor/lib64"); #else From 0e8587af0bcd39a961c339c45a9cd39a79bc68a2 Mon Sep 17 00:00:00 2001 From: "Petrov, Nikolay A" Date: Wed, 21 Oct 2026 20:39:35 -0700 Subject: [PATCH 2/2] loader: rename to L0_LOADER_EXTRA_DRIVER_DIR, dlopen discovered full path Rename the build-time option to L0_LOADER_EXTRA_DRIVER_DIR so it does not look like a ZE_* runtime environment variable. Discovery on Linux used to check that a known driver existed in one of the search paths and then dlopen() the bare soname, leaving the dynamic linker to pick the file. The file that was checked and the file that was loaded could therefore differ, and a directory only on the search-path list (such as the build-time extra directory) passed the check without being reachable by dlopen(). Return the full path of the first match and load that path instead. Co-Authored-By: Claude Opus 5.5 --- source/loader/CMakeLists.txt | 100 ++--- source/loader/linux/driver_discovery_lin.cpp | 389 ++++++++++--------- 2 files changed, 247 insertions(+), 242 deletions(-) diff --git a/source/loader/CMakeLists.txt b/source/loader/CMakeLists.txt index b7943afc..2a013a26 100644 --- a/source/loader/CMakeLists.txt +++ b/source/loader/CMakeLists.txt @@ -1,50 +1,50 @@ -# Copyright (C) 2020-2025 Intel Corporation -# SPDX-License-Identifier: MIT - - -target_sources(${TARGET_LOADER_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/ze_object.h - ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader_internal.h - ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader_api.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi.h - ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi_driver_ddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi.h - ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi_driver_ddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi.h - ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi_driver_ddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi.h - ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi_driver_ddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/zel_tracing_ldrddi.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/driver_discovery.h -) - -if(WIN32) - target_sources(${TARGET_LOADER_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/windows/driver_discovery_win.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/windows/loader_init.cpp - ) -else() - target_sources(${TARGET_LOADER_NAME} - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/linux/driver_discovery_lin.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/linux/loader_init.cpp - ) -endif() - -# An optional build-time configured directory to search for drivers, in -# addition to LD_LIBRARY_PATH and the system locations. Packaging (e.g. -# conda-forge) sets this to the install prefix's lib dir so that a driver -# co-installed in the same prefix is discovered by default. CMake handles the -# string quoting for the compile definition. -if(DEFINED ZE_LOADER_EXTRA_DRIVER_DIR AND NOT ZE_LOADER_EXTRA_DRIVER_DIR STREQUAL "") - target_compile_definitions(${TARGET_LOADER_NAME} - PRIVATE ZE_LOADER_EXTRA_DRIVER_DIR="${ZE_LOADER_EXTRA_DRIVER_DIR}") -endif() - +# Copyright (C) 2020-2025 Intel Corporation +# SPDX-License-Identifier: MIT + + +target_sources(${TARGET_LOADER_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/ze_object.h + ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader_internal.h + ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ze_loader_api.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi.h + ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi_driver_ddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ze_ldrddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi.h + ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi_driver_ddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zet_ldrddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi.h + ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi_driver_ddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zes_ldrddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi.h + ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi_driver_ddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zer_ldrddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/zel_tracing_ldrddi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/driver_discovery.h +) + +if(WIN32) + target_sources(${TARGET_LOADER_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/windows/driver_discovery_win.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/windows/loader_init.cpp + ) +else() + target_sources(${TARGET_LOADER_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/linux/driver_discovery_lin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/linux/loader_init.cpp + ) +endif() + +# An optional build-time configured directory to search for drivers, in +# addition to LD_LIBRARY_PATH and the system locations. Packaging (e.g. +# conda-forge) sets this to the install prefix's lib dir so that a driver +# co-installed in the same prefix is discovered by default. CMake handles the +# string quoting for the compile definition. +if(DEFINED L0_LOADER_EXTRA_DRIVER_DIR AND NOT L0_LOADER_EXTRA_DRIVER_DIR STREQUAL "") + target_compile_definitions(${TARGET_LOADER_NAME} + PRIVATE L0_LOADER_EXTRA_DRIVER_DIR="${L0_LOADER_EXTRA_DRIVER_DIR}") +endif() + diff --git a/source/loader/linux/driver_discovery_lin.cpp b/source/loader/linux/driver_discovery_lin.cpp index a200a14d..eb1bffdc 100644 --- a/source/loader/linux/driver_discovery_lin.cpp +++ b/source/loader/linux/driver_discovery_lin.cpp @@ -1,192 +1,197 @@ -/* - * - * Copyright (C) 2020 Intel Corporation - * - * SPDX-License-Identifier: MIT - * - */ - -#include "source/loader/driver_discovery.h" - -#include "source/inc/ze_util.h" -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -// Helper to split a colon-separated path string -static std::vector splitPaths(const std::string& paths) { - std::vector result; - std::stringstream ss(paths); - std::string item; - while (std::getline(ss, item, ':')) { - if (!item.empty()) result.push_back(item); - } - return result; -} - -// Helper to check if a file exists and is readable -static bool fileExistsReadable(const std::string& path) { - struct stat sb; - return (stat(path.c_str(), &sb) == 0) && (access(path.c_str(), R_OK) == 0); -} - -// Helper to get all library search paths from LD_LIBRARY_PATH, standard locations, and /etc/ld.so.conf -static std::vector getLibrarySearchPaths() { - std::vector paths; - // LD_LIBRARY_PATH - const char* ldLibPath = getenv("LD_LIBRARY_PATH"); - if (ldLibPath) { - auto split = splitPaths(ldLibPath); - paths.insert(paths.end(), split.begin(), split.end()); - } -#ifdef ZE_LOADER_EXTRA_DRIVER_DIR - // A driver directory configured at build time (e.g. the install prefix's lib - // dir, baked in at compile time and relocated on install). Searched right - // after LD_LIBRARY_PATH -- so a user's LD_LIBRARY_PATH still takes precedence - // -- but before the system locations. This lets a driver co-installed in the - // same prefix (e.g. a conda environment) be discovered by default, without - // having to set ZE_ENABLE_ALT_DRIVERS. - paths.push_back(ZE_LOADER_EXTRA_DRIVER_DIR); -#endif -#if defined(ANDROID) - paths.push_back("/vendor/lib64"); -#else - // Standard locations - Common across all Linux distributions - paths.push_back("/lib"); - paths.push_back("/usr/lib"); - paths.push_back("/usr/local/lib"); - - // Multi-arch paths for Ubuntu/Debian - paths.push_back("/lib/x86_64-linux-gnu"); - paths.push_back("/usr/lib/x86_64-linux-gnu"); - paths.push_back("/lib/i386-linux-gnu"); - paths.push_back("/usr/lib/i386-linux-gnu"); - paths.push_back("/lib/aarch64-linux-gnu"); - paths.push_back("/usr/lib/aarch64-linux-gnu"); - - // 64-bit library paths for RHEL/CentOS/Fedora - paths.push_back("/lib64"); - paths.push_back("/usr/lib64"); - paths.push_back("/usr/local/lib64"); - - // Flatpak/Snap paths for containerized applications - paths.push_back("/var/lib/flatpak/runtime"); - paths.push_back("/snap/core/current/lib"); - paths.push_back("/snap/core/current/usr/lib"); -#endif // ANDROID - - // /etc/ld.so.conf and included files - std::ifstream ldSoConf("/etc/ld.so.conf"); - if (ldSoConf) { - std::string line; - while (std::getline(ldSoConf, line)) { - if (line.empty()) continue; - if (line.find("include ") == 0) { - std::string pattern = line.substr(8); - // Simple glob: /etc/ld.so.conf.d/*.conf - std::string dir = pattern.substr(0, pattern.find_last_of('/')); - std::string ext = pattern.substr(pattern.find_last_of('.')); - DIR* d = opendir(dir.c_str()); - if (d) { - struct dirent* ent; - while ((ent = readdir(d)) != nullptr) { - std::string fname = ent->d_name; - if (fname.size() > ext.size() && fname.substr(fname.size()-ext.size()) == ext) { - std::ifstream incFile(dir + "/" + fname); - std::string incLine; - while (std::getline(incFile, incLine)) { - if (!incLine.empty() && incLine[0] != '#') - paths.push_back(incLine); - } - } - } - closedir(d); - } - } else if (line[0] != '#') { - paths.push_back(line); - } - } - } - return paths; -} - -// Main function: search for a library file in all known library paths -static bool libraryExistsInSearchPaths(const std::string& filename) { - auto paths = getLibrarySearchPaths(); - for (const auto& dir : paths) { - std::string fullPath = dir + "/" + filename; - if (fileExistsReadable(fullPath)) { - return true; - } - } - return false; -} - -namespace loader { - -static const char *knownDriverNames[] = { - MAKE_LIBRARY_NAME("ze_intel_gpu", "1"), - MAKE_LIBRARY_NAME("ze_intel_gpu_legacy1", "1"), - MAKE_LIBRARY_NAME("ze_intel_vpu", "1"), - MAKE_LIBRARY_NAME("ze_intel_npu", "1"), -}; - -std::vector discoverEnabledDrivers() { - std::vector enabledDrivers; - const char *altDrivers = nullptr; - - // ZE_ENABLE_ALT_DRIVERS is for development/debug only - altDrivers = getenv("ZE_ENABLE_ALT_DRIVERS"); - if (altDrivers == nullptr) { - // Standard drivers - not custom - for (auto path : knownDriverNames) { - if (libraryExistsInSearchPaths(path)) { - // Extract the base library name for robust driver type detection - // path is like "libze_intel_gpu.so.1" - std::string libName = path; - - // Remove "lib" prefix if present - if (libName.compare(0, 3, "lib") == 0) { - libName = libName.substr(3); - } - - // Remove file extension - size_t dotPos = libName.find('.'); - if (dotPos != std::string::npos) { - libName = libName.substr(0, dotPos); - } - - // Now match against the core driver name (e.g., "ze_intel_gpu", "ze_intel_npu") - // Check for exact matches or word boundaries to avoid substring false positives - zel_driver_type_t driverType = ZEL_DRIVER_TYPE_FORCE_UINT32; - - // Check more specific patterns first to avoid partial matches - if (libName == "ze_intel_gpu" || libName == "ze_intel_gpu_legacy1") { - driverType = ZEL_DRIVER_TYPE_GPU; - } else if (libName == "ze_intel_vpu" || libName == "ze_intel_npu") { - driverType = ZEL_DRIVER_TYPE_NPU; - } - - enabledDrivers.emplace_back(path, false, driverType); - } - } - } else { - // Alternative drivers from environment variable - these are custom - std::stringstream ss(altDrivers); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - enabledDrivers.emplace_back(substr, true); - } - } - return enabledDrivers; -} - -} // namespace loader +/* + * + * Copyright (C) 2020 Intel Corporation + * + * SPDX-License-Identifier: MIT + * + */ + +#include "source/loader/driver_discovery.h" + +#include "source/inc/ze_util.h" +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +// Helper to split a colon-separated path string +static std::vector splitPaths(const std::string& paths) { + std::vector result; + std::stringstream ss(paths); + std::string item; + while (std::getline(ss, item, ':')) { + if (!item.empty()) result.push_back(item); + } + return result; +} + +// Helper to check if a file exists and is readable +static bool fileExistsReadable(const std::string& path) { + struct stat sb; + return (stat(path.c_str(), &sb) == 0) && (access(path.c_str(), R_OK) == 0); +} + +// Helper to get all library search paths from LD_LIBRARY_PATH, standard locations, and /etc/ld.so.conf +static std::vector getLibrarySearchPaths() { + std::vector paths; + // LD_LIBRARY_PATH + const char* ldLibPath = getenv("LD_LIBRARY_PATH"); + if (ldLibPath) { + auto split = splitPaths(ldLibPath); + paths.insert(paths.end(), split.begin(), split.end()); + } +#ifdef L0_LOADER_EXTRA_DRIVER_DIR + // A driver directory configured at build time (e.g. the install prefix's lib + // dir, baked in at compile time and relocated on install). Searched right + // after LD_LIBRARY_PATH -- so a user's LD_LIBRARY_PATH still takes precedence + // -- but before the system locations. This lets a driver co-installed in the + // same prefix (e.g. a conda environment) be discovered by default, without + // having to set ZE_ENABLE_ALT_DRIVERS. + paths.push_back(L0_LOADER_EXTRA_DRIVER_DIR); +#endif +#if defined(ANDROID) + paths.push_back("/vendor/lib64"); +#else + // Standard locations - Common across all Linux distributions + paths.push_back("/lib"); + paths.push_back("/usr/lib"); + paths.push_back("/usr/local/lib"); + + // Multi-arch paths for Ubuntu/Debian + paths.push_back("/lib/x86_64-linux-gnu"); + paths.push_back("/usr/lib/x86_64-linux-gnu"); + paths.push_back("/lib/i386-linux-gnu"); + paths.push_back("/usr/lib/i386-linux-gnu"); + paths.push_back("/lib/aarch64-linux-gnu"); + paths.push_back("/usr/lib/aarch64-linux-gnu"); + + // 64-bit library paths for RHEL/CentOS/Fedora + paths.push_back("/lib64"); + paths.push_back("/usr/lib64"); + paths.push_back("/usr/local/lib64"); + + // Flatpak/Snap paths for containerized applications + paths.push_back("/var/lib/flatpak/runtime"); + paths.push_back("/snap/core/current/lib"); + paths.push_back("/snap/core/current/usr/lib"); +#endif // ANDROID + + // /etc/ld.so.conf and included files + std::ifstream ldSoConf("/etc/ld.so.conf"); + if (ldSoConf) { + std::string line; + while (std::getline(ldSoConf, line)) { + if (line.empty()) continue; + if (line.find("include ") == 0) { + std::string pattern = line.substr(8); + // Simple glob: /etc/ld.so.conf.d/*.conf + std::string dir = pattern.substr(0, pattern.find_last_of('/')); + std::string ext = pattern.substr(pattern.find_last_of('.')); + DIR* d = opendir(dir.c_str()); + if (d) { + struct dirent* ent; + while ((ent = readdir(d)) != nullptr) { + std::string fname = ent->d_name; + if (fname.size() > ext.size() && fname.substr(fname.size()-ext.size()) == ext) { + std::ifstream incFile(dir + "/" + fname); + std::string incLine; + while (std::getline(incFile, incLine)) { + if (!incLine.empty() && incLine[0] != '#') + paths.push_back(incLine); + } + } + } + closedir(d); + } + } else if (line[0] != '#') { + paths.push_back(line); + } + } + } + return paths; +} + +// Main function: search for a library file in all known library paths. +// Returns the full path of the first match, or an empty string if none. +// The full path is what gets passed to dlopen(), so the file that is loaded +// is the one that was found here, not whatever the dynamic linker resolves +// the bare soname to. +static std::string findLibraryInSearchPaths(const std::string& filename) { + auto paths = getLibrarySearchPaths(); + for (const auto& dir : paths) { + std::string fullPath = dir + "/" + filename; + if (fileExistsReadable(fullPath)) { + return fullPath; + } + } + return std::string(); +} + +namespace loader { + +static const char *knownDriverNames[] = { + MAKE_LIBRARY_NAME("ze_intel_gpu", "1"), + MAKE_LIBRARY_NAME("ze_intel_gpu_legacy1", "1"), + MAKE_LIBRARY_NAME("ze_intel_vpu", "1"), + MAKE_LIBRARY_NAME("ze_intel_npu", "1"), +}; + +std::vector discoverEnabledDrivers() { + std::vector enabledDrivers; + const char *altDrivers = nullptr; + + // ZE_ENABLE_ALT_DRIVERS is for development/debug only + altDrivers = getenv("ZE_ENABLE_ALT_DRIVERS"); + if (altDrivers == nullptr) { + // Standard drivers - not custom + for (auto path : knownDriverNames) { + std::string fullPath = findLibraryInSearchPaths(path); + if (!fullPath.empty()) { + // Extract the base library name for robust driver type detection + // path is like "libze_intel_gpu.so.1" + std::string libName = path; + + // Remove "lib" prefix if present + if (libName.compare(0, 3, "lib") == 0) { + libName = libName.substr(3); + } + + // Remove file extension + size_t dotPos = libName.find('.'); + if (dotPos != std::string::npos) { + libName = libName.substr(0, dotPos); + } + + // Now match against the core driver name (e.g., "ze_intel_gpu", "ze_intel_npu") + // Check for exact matches or word boundaries to avoid substring false positives + zel_driver_type_t driverType = ZEL_DRIVER_TYPE_FORCE_UINT32; + + // Check more specific patterns first to avoid partial matches + if (libName == "ze_intel_gpu" || libName == "ze_intel_gpu_legacy1") { + driverType = ZEL_DRIVER_TYPE_GPU; + } else if (libName == "ze_intel_vpu" || libName == "ze_intel_npu") { + driverType = ZEL_DRIVER_TYPE_NPU; + } + + enabledDrivers.emplace_back(fullPath, false, driverType); + } + } + } else { + // Alternative drivers from environment variable - these are custom + std::stringstream ss(altDrivers); + while (ss.good()) { + std::string substr; + getline(ss, substr, ','); + enabledDrivers.emplace_back(substr, true); + } + } + return enabledDrivers; +} + +} // namespace loader