diff --git a/mlx/backend/common/utils.cpp b/mlx/backend/common/utils.cpp index ae169e35e2..5598076eb4 100644 --- a/mlx/backend/common/utils.cpp +++ b/mlx/backend/common/utils.cpp @@ -12,7 +12,19 @@ std::filesystem::path current_binary_dir() { if (!dladdr(reinterpret_cast(¤t_binary_dir), &info)) { throw std::runtime_error("Unable to get current binary dir."); } - return std::filesystem::path(info.dli_fname).parent_path(); + // glibc reports dli_fname for a main executable as argv[0] verbatim, so it + // may be relative or contain "."; resolve it so that parent_path() of the + // result names the real parent directory. + std::filesystem::path path(info.dli_fname); + if (!path.has_parent_path()) { + // A bare argv[0] carries no location, and the two standard libraries + // disagree about it: libc++ would resolve it against the current working + // directory, inventing an unrelated answer. + return path.parent_path(); + } + std::error_code ec; + auto resolved = std::filesystem::weakly_canonical(path, ec); + return ec ? path.parent_path() : resolved.parent_path(); }(); return binary_dir; } diff --git a/mlx/backend/common/utils.h b/mlx/backend/common/utils.h index c6d7820619..b1873493b7 100644 --- a/mlx/backend/common/utils.h +++ b/mlx/backend/common/utils.h @@ -6,12 +6,15 @@ #include #include +#include "mlx/api.h" #include "mlx/array.h" namespace mlx::core { -// Return the directory that contains current shared library. -std::filesystem::path current_binary_dir(); +// Return the resolved directory that contains the current binary, with ".", +// ".." and symlinks removed. Empty when the loader reports no location for +// it, which glibc does for a PATH-launched executable. +MLX_API std::filesystem::path current_binary_dir(); inline int64_t elem_to_loc(int elem, const Shape& shape, const Strides& strides) { diff --git a/tests/utils_tests.cpp b/tests/utils_tests.cpp index 88c3e7b378..38a18bb3fd 100644 --- a/tests/utils_tests.cpp +++ b/tests/utils_tests.cpp @@ -2,10 +2,22 @@ #include "doctest/doctest.h" +#include "mlx/backend/common/utils.h" #include "mlx/mlx.h" using namespace mlx::core; +TEST_CASE("test current binary dir is resolved") { + auto dir = current_binary_dir(); + // Empty is a legal result: a PATH-launched executable gets a bare argv[0], + // which carries no location to resolve. + if (dir.empty()) { + return; + } + CHECK(dir.is_absolute()); + CHECK_EQ(dir, dir.lexically_normal()); +} + TEST_CASE("test type promotion") { for (auto t : {bool_, uint32, int32, int64, float32}) { auto a = array(0, t);