You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C程序中查询当前进程的库搜索路径?

在FreeBSD的C程序中查询当前进程的库搜索路径(用于Capsicum沙箱)

问题背景

我正在尝试基于Capsicum创建沙箱来加载插件,目前大部分流程已经跑通:

  • 创建通信管道
  • 调用fork()

子进程侧的操作步骤:

  • 关闭不需要的资源
  • 调用cap_enter()
  • 调用fdlopen()

当插件是自包含状态(所需符号都在主程序中)时,整个流程正常工作。但如果插件依赖系统库,加载就会失败。FreeBSD的rtld提供了LD_LIBRARY_PATH_FDS环境变量,作为Capsicum模式下可用的额外库路径,我可以在子进程的C程序中设置这个变量。

现在需要在调用cap_enter()前查询当前进程的库搜索路径,打开对应的文件描述符,请问如何在C程序中实现这个查询?


解决方案

方法1:用dl_iterate_phdr遍历已加载的动态库

FreeBSD提供的dl_iterate_phdr()函数可以遍历当前进程加载的所有动态链接库信息,从中提取库所在目录路径。示例代码如下:

#define _GNU_SOURCE
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

typedef struct {
    char **paths;
    size_t count;
    size_t capacity;
} LibPathCollector;

static int collect_lib_dirs(struct dl_phdr_info *info, size_t size, void *data) {
    LibPathCollector *collector = (LibPathCollector *)data;
    char *lib_path = strdup(info->dlpi_name);
    if (lib_path == NULL) {
        perror("strdup failed");
        return 1;
    }

    // 跳过主程序本身的空路径
    if (strlen(lib_path) == 0) {
        free(lib_path);
        return 0;
    }

    // 提取库所在的目录
    char *dir_path = strdup(dirname(lib_path));
    free(lib_path);
    if (dir_path == NULL) {
        perror("strdup dirname failed");
        return 1;
    }

    // 去重,避免重复添加相同目录
    int exists = 0;
    for (size_t i = 0; i < collector->count; i++) {
        if (strcmp(collector->paths[i], dir_path) == 0) {
            exists = 1;
            break;
        }
    }
    if (exists) {
        free(dir_path);
        return 0;
    }

    // 动态扩容数组
    if (collector->count >= collector->capacity) {
        collector->capacity = collector->capacity == 0 ? 8 : collector->capacity * 2;
        char **new_paths = realloc(collector->paths, collector->capacity * sizeof(char *));
        if (new_paths == NULL) {
            perror("realloc failed");
            free(dir_path);
            return 1;
        }
        collector->paths = new_paths;
    }

    collector->paths[collector->count++] = dir_path;
    collector->paths[collector->count] = NULL;
    return 0;
}

char **get_current_lib_dirs(void) {
    LibPathCollector collector = {NULL, 0, 0};
    if (dl_iterate_phdr(collect_lib_dirs, &collector) != 0) {
        // 遍历出错,清理已分配内存
        if (collector.paths != NULL) {
            for (size_t i = 0; i < collector.count; i++) {
                free(collector.paths[i]);
            }
            free(collector.paths);
        }
        return NULL;
    }
    return collector.paths;
}

// 使用示例
int main() {
    char **lib_dirs = get_current_lib_dirs();
    if (lib_dirs == NULL) {
        fprintf(stderr, "Failed to collect library directories\n");
        return 1;
    }

    // 打开每个目录的文件描述符,用于后续设置LD_LIBRARY_PATH_FDS
    int *fds = malloc(sizeof(int) * 16); // 按需调整大小
    size_t fd_count = 0;
    for (size_t i = 0; lib_dirs[i] != NULL; i++) {
        int fd = open(lib_dirs[i], O_RDONLY | O_DIRECTORY);
        if (fd != -1) {
            fds[fd_count++] = fd;
        } else {
            fprintf(stderr, "Failed to open %s: %s\n", lib_dirs[i], strerror(errno));
        }
    }

    // 构建LD_LIBRARY_PATH_FDS的值
    char fd_str[256] = {0};
    for (size_t i = 0; i < fd_count; i++) {
        if (i > 0) strcat(fd_str, ":");
        char buf[16];
        snprintf(buf, sizeof(buf), "%d", fds[i]);
        strcat(fd_str, buf);
    }
    setenv("LD_LIBRARY_PATH_FDS", fd_str, 1);

    // 后续执行cap_enter()和fdlopen()...

    // 资源清理
    for (size_t i = 0; lib_dirs[i] != NULL; i++) {
        free(lib_dirs[i]);
    }
    free(lib_dirs);
    free(fds);
    return 0;
}

方法2:读取/proc/self/maps(需挂载procfs)

如果系统已经挂载procfs(通常在/proc目录),可以读取/proc/self/maps文件解析已加载库的路径,再提取目录:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char **get_lib_dirs_from_proc(void) {
    FILE *f = fopen("/proc/self/maps", "r");
    if (f == NULL) {
        perror("fopen /proc/self/maps failed");
        return NULL;
    }

    char **dirs = NULL;
    size_t count = 0;
    char line[1024];

    while (fgets(line, sizeof(line), f) != NULL) {
        // 定位路径起始位置(以/开头)
        char *path_start = strchr(line, '/');
        if (path_start == NULL) continue;

        // 去除换行符
        size_t len = strlen(path_start);
        if (path_start[len-1] == '\n') path_start[len-1] = '\0';

        // 仅处理.so结尾的动态库文件
        if (strstr(path_start, ".so") == NULL) continue;

        // 提取目录
        char *dir_path = strdup(dirname(path_start));
        if (dir_path == NULL) {
            perror("strdup dirname failed");
            goto cleanup;
        }

        // 去重
        int exists = 0;
        for (size_t i = 0; i < count; i++) {
            if (strcmp(dirs[i], dir_path) == 0) {
                exists = 1;
                break;
            }
        }
        if (exists) {
            free(dir_path);
            continue;
        }

        // 扩容数组
        dirs = realloc(dirs, (count + 2) * sizeof(char *));
        if (dirs == NULL) {
            perror("realloc failed");
            free(dir_path);
            goto cleanup;
        }
        dirs[count++] = dir_path;
        dirs[count] = NULL;
    }

    fclose(f);
    return dirs;

cleanup:
    if (dirs != NULL) {
        for (size_t i = 0; i < count; i++) free(dirs[i]);
        free(dirs);
    }
    fclose(f);
    return NULL;
}

方法3:调用rtld内部函数_rtld_get_paths()

FreeBSD的rtld提供了内部函数_rtld_get_paths(),可以直接获取系统默认的库搜索路径,需要链接时加上-ldl参数:

#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

// 声明rtld内部函数
extern char **_rtld_get_paths(void);

int main() {
    char **default_paths = _rtld_get_paths();
    if (default_paths == NULL) {
        fprintf(stderr, "Failed to get default library paths\n");
        return 1;
    }

    // 打开目录fd并设置LD_LIBRARY_PATH_FDS
    int *fds = malloc(sizeof(int) * 8);
    size_t fd_count = 0;
    for (size_t i = 0; default_paths[i] != NULL; i++) {
        int fd = open(default_paths[i], O_RDONLY | O_DIRECTORY);
        if (fd != -1) {
            fds[fd_count++] = fd;
        }
    }

    // 构建环境变量值
    char fd_str[128] = {0};
    for (size_t i = 0; i < fd_count; i++) {
        if (i > 0) strcat(fd_str, ":");
        char buf[8];
        snprintf(buf, sizeof(buf), "%d", fds[i]);
        strcat(fd_str, buf);
    }
    setenv("LD_LIBRARY_PATH_FDS", fd_str, 1);

    // 后续执行cap_enter()和fdlopen()...

    free(fds);
    return 0;
}

注意:_rtld_get_paths()是内部函数,不同FreeBSD版本可能存在兼容性问题,需要测试验证。


内容的提问来源于stack exchange,提问作者vinipsmaker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:40:20