在 MacOS Big Sur 上检查库是否存在
Checking for library existence on MacOS Big Sur
有没有一种方法可以复制 _dyld_shared_cache_contains_path
的行为,同时适用于 MacOS Big Sur 和 MacOS Catalina?
我的第一次尝试(使用 dlopen)
#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <stdio.h>
int library_exists(const char* path) {
void* handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) return 0;
dlclose(handle);
return 1;
}
int main(void) {
int result;
char* path;
path = "/usr/lib/libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d", path, result);
path = "libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d", path, result);
}
输出:
_dyld_shared_cache_contains_path(/usr/lib/libc.dylib) == 1
library_exists(/usr/lib/libc.dylib) == 1
_dyld_shared_cache_contains_path(libc.dylib) == 0
library_exists(libc.dylib) == 1
这非常接近,除了“libc.dylib”在传递给 2 个函数时有不同的行为。
背景
MacOS Big Sur 从文件系统中删除了共享库,并将它们放入缓存中。函数 _dyld_shared_cache_contains_path 随此更改在 中可用。
MacOS Big Sur 11.0.1 changenotes
New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)
我想要一个可移植二进制文件来检查 MacOS Catalina 或 MacOS Big Sur 上是否存在共享库,而无需针对特定版本的 MacOS 重新编译。如果我们引用 _dyld_shared_cache_contains_path 并尝试在 MacOS Catalina 上编译 - 编译将失败。我希望它具有与 _dyld_shared_cache_contains_path
.
相同的行为
您可以通过设置一堆环境变量来修复 dlopen
...但是您必须生成一个新进程。
man dlopen
有更详尽的描述,但基本上你看到的是后备效果,因为 libc.dylib
只是一个“叶名”,dyld 搜索环境指定的一堆路径该库的变量。
以下环境变量会影响此行为:
DYLD_LIBRARY_PATH
DYLD_FRAMEWORK_PATH
DYLD_FALLBACK_LIBRARY_PATH
DYLD_FALLBACK_FRAMEWORK_PATH
DYLD_IMAGE_SUFFIX
如果未设置,则两个 FALLBACK
默认为某些系统路径 - 联机帮助页在这方面不是最新的,但确切的值可以包括以下路径或其子集,基于在内核设置的标志上:
/usr/local/lib
/usr/lib
/Library/Frameworks
/System/Library/Frameworks
DYLD_IMAGE_SUFFIX
有点不同,但是如果你把它设置为.dylib
,那么dlopen("libc")
就会成功,所以这也是不需要的。
您可以通过将这些环境变量设置为空字符串来禁用所有回退行为。
因此,如果您像 DYLD_FALLBACK_LIBRARY_PATH='' ./test
那样调用上面的代码,它将按预期工作:
_dyld_shared_cache_contains_path(/usr/lib/libc.dylib) == 1
library_exists(/usr/lib/libc.dylib) == 1
_dyld_shared_cache_contains_path(libc.dylib) == 0
library_exists(libc.dylib) == 0
但是,如果您在代码中执行 setenv("DYLD_FALLBACK_LIBRARY_PATH", "", 1);
,您会发现它不起作用。原因是 dyld 只在进程初始化时查看环境变量,之后的任何内容都将被忽略。
这是一个使用 execve
的工作示例:
#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern char **environ;
int library_exists(const char* path) {
void* handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) return 0;
dlclose(handle);
return 1;
}
int main(int argc, const char **argv) {
int result;
char* path;
const char *vars[] = { "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH", "DYLD_FALLBACK_LIBRARY_PATH", "DYLD_FALLBACK_FRAMEWORK_PATH", "DYLD_IMAGE_SUFFIX" };
char fail = 0;
for(size_t i = 0; i < 5; ++i)
{
char *v = getenv(vars[i]);
if(!v || *v != '[=11=]')
{
fail = 1;
setenv(vars[i], "", 1);
}
}
if(fail)
{
return execve(argv[0], (char*const*)argv, environ);
}
path = "/usr/lib/libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d\n", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d\n", path, result);
path = "libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d\n", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d\n", path, result);
}
有没有一种方法可以复制 _dyld_shared_cache_contains_path
的行为,同时适用于 MacOS Big Sur 和 MacOS Catalina?
我的第一次尝试(使用 dlopen)
#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <stdio.h>
int library_exists(const char* path) {
void* handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) return 0;
dlclose(handle);
return 1;
}
int main(void) {
int result;
char* path;
path = "/usr/lib/libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d", path, result);
path = "libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d", path, result);
}
输出:
_dyld_shared_cache_contains_path(/usr/lib/libc.dylib) == 1
library_exists(/usr/lib/libc.dylib) == 1
_dyld_shared_cache_contains_path(libc.dylib) == 0
library_exists(libc.dylib) == 1
这非常接近,除了“libc.dylib”在传递给 2 个函数时有不同的行为。
背景
MacOS Big Sur 从文件系统中删除了共享库,并将它们放入缓存中。函数 _dyld_shared_cache_contains_path 随此更改在
MacOS Big Sur 11.0.1 changenotes
New in macOS Big Sur 11.0.1, the system ships with a built-in dynamic linker cache of all system-provided libraries. As part of this change, copies of dynamic libraries are no longer present on the filesystem. Code that attempts to check for dynamic library presence by looking for a file at a path or enumerating a directory will fail. Instead, check for library presence by attempting to dlopen() the path, which will correctly check for the library in the cache. (62986286)
我想要一个可移植二进制文件来检查 MacOS Catalina 或 MacOS Big Sur 上是否存在共享库,而无需针对特定版本的 MacOS 重新编译。如果我们引用 _dyld_shared_cache_contains_path 并尝试在 MacOS Catalina 上编译 - 编译将失败。我希望它具有与 _dyld_shared_cache_contains_path
.
您可以通过设置一堆环境变量来修复 dlopen
...但是您必须生成一个新进程。
man dlopen
有更详尽的描述,但基本上你看到的是后备效果,因为 libc.dylib
只是一个“叶名”,dyld 搜索环境指定的一堆路径该库的变量。
以下环境变量会影响此行为:
DYLD_LIBRARY_PATH
DYLD_FRAMEWORK_PATH
DYLD_FALLBACK_LIBRARY_PATH
DYLD_FALLBACK_FRAMEWORK_PATH
DYLD_IMAGE_SUFFIX
如果未设置,则两个 FALLBACK
默认为某些系统路径 - 联机帮助页在这方面不是最新的,但确切的值可以包括以下路径或其子集,基于在内核设置的标志上:
/usr/local/lib
/usr/lib
/Library/Frameworks
/System/Library/Frameworks
DYLD_IMAGE_SUFFIX
有点不同,但是如果你把它设置为.dylib
,那么dlopen("libc")
就会成功,所以这也是不需要的。
您可以通过将这些环境变量设置为空字符串来禁用所有回退行为。
因此,如果您像 DYLD_FALLBACK_LIBRARY_PATH='' ./test
那样调用上面的代码,它将按预期工作:
_dyld_shared_cache_contains_path(/usr/lib/libc.dylib) == 1
library_exists(/usr/lib/libc.dylib) == 1
_dyld_shared_cache_contains_path(libc.dylib) == 0
library_exists(libc.dylib) == 0
但是,如果您在代码中执行 setenv("DYLD_FALLBACK_LIBRARY_PATH", "", 1);
,您会发现它不起作用。原因是 dyld 只在进程初始化时查看环境变量,之后的任何内容都将被忽略。
这是一个使用 execve
的工作示例:
#include <mach-o/dyld.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern char **environ;
int library_exists(const char* path) {
void* handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) return 0;
dlclose(handle);
return 1;
}
int main(int argc, const char **argv) {
int result;
char* path;
const char *vars[] = { "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH", "DYLD_FALLBACK_LIBRARY_PATH", "DYLD_FALLBACK_FRAMEWORK_PATH", "DYLD_IMAGE_SUFFIX" };
char fail = 0;
for(size_t i = 0; i < 5; ++i)
{
char *v = getenv(vars[i]);
if(!v || *v != '[=11=]')
{
fail = 1;
setenv(vars[i], "", 1);
}
}
if(fail)
{
return execve(argv[0], (char*const*)argv, environ);
}
path = "/usr/lib/libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d\n", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d\n", path, result);
path = "libc.dylib";
result = _dyld_shared_cache_contains_path(path);
printf("_dyld_shared_cache_contains_path(%s) == %d\n", path, result);
result = library_exists(path);
printf("library_exists(%s) == %d\n", path, result);
}