簡體   English   中英

檢查 MacOS Big Sur 上的庫是否存在

[英]Checking for library existence on MacOS Big Sur

有沒有一種方法可以復制_dyld_shared_cache_contains_path的行為, _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 已在 <mach-o/dyld.h> 中隨此更改一起提供。

MacOS Big Sur 11.0.1 變更說明

macOS Big Sur 11.0.1 中的新功能,系統附帶所有系統提供的庫的內置動態鏈接器緩存。 作為此更改的一部分,文件系統上不再存在動態庫的副本。 嘗試通過在路徑中查找文件或枚舉目錄來檢查動態庫是否存在的代碼將失敗。 相反,通過嘗試 dlopen() 路徑來檢查庫是否存在,這將正確檢查緩存中的庫。 (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 != '\0')
        {
            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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM