c - 如何在库加载时将参数传递给构造函数?

标签 c linux gcc constructor shared-libraries

我正在尝试在 Linux 中创建一个共享库。加载库时如何将参数传递给函数 my_load()?在我的 C 应用程序中,我调用了 test_func() 然后它在被调用的函数之前首先自动执行 my_load() 然后最后它执行 my_unload()

#include <stdio.h>

void __attribute__ ((constructor)) my_load(int argc, char *argv[]);
void __attribute__ ((destructor)) my_unload(void);
void test_func(void);

void my_load(int argc, char *argv[]) {
printf("my_load: %d\n", argc);
}

void my_unload(void) {
printf("my_unload\n");
}

void test_func(void) {
printf("test_func()\n");
}

最佳答案

你的动态库总是可以读取/proc/self/cmdline查看用于执行当前可执行文件的命令行参数是什么。 example.c:

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

static char **get_argv(int *const argcptr)
{
    char  **argv;
    char   *data = NULL;
    size_t  size = 0;    /* Allocated to data */
    size_t  used = 0;
    size_t  argc, i;
    ssize_t bytes;
    int     fd;

    if (argcptr)
        *argcptr = 0;

    do {
        fd = open("/proc/self/cmdline", O_RDONLY | O_NOCTTY);
    } while (fd == -1 && errno == EINTR);
    if (fd == -1)
        return NULL;

    while (1) {

        if (used >= size) {
            char *old_data = data;
            size = (used | 4095) + 4096;
            data = realloc(data, size + 1);
            if (data == NULL) {
                free(old_data);
                close(fd);
                errno = ENOMEM;
                return NULL;
            }
        }

        do {
            bytes = read(fd, data + used, size - used);
        } while (bytes == (ssize_t)-1 && errno == EINTR);
        if (bytes < (ssize_t)0) {
            free(data);
            close(fd);
            errno = EIO;
            return NULL;

        } else
        if (bytes == (ssize_t)0)
            break;

        else
            used += bytes;
    }

    if (close(fd)) {
        free(data);
        errno = EIO;
        return NULL;
    }

    /* Let's be safe and overallocate one pointer here. */
    argc = 1;
    for (i = 0; i < used; i++)
        if (data[i] == '\0')
            argc++;

    /* Reallocate to accommodate both pointers and data. */
    argv = realloc(data, (argc + 1) * sizeof (char *) + used + 1);
    if (argv == NULL) {
        free(data);
        errno = ENOMEM;
        return NULL;
    }
    data = (char *)(argv + argc + 1);
    memmove(data, argv, used);

    /* In case the input lacked a trailing NUL byte. */
    data[used] = '\0';

    /* Assign the pointers. */
    argv[0] = data;
    argc = 0;
    for (i = 0; i < used; i++)
        if (data[i] == '\0')
            argv[++argc] = data + i + 1;
    /* Final pointer points to past data. Make it end the array. */
    argv[argc] = NULL;

    if (argcptr)
        *argcptr = (int)argc;

    return argv;
}

/* Example standard error functions, that avoid the use of stdio.h.
*/
static void wrerr(const char *p)
{
    if (p != NULL) {
        const char *const q = p + strlen(p);
        ssize_t           n;

        while (p < q) {
            n = write(STDERR_FILENO, p, (size_t)(q - p));
            if (n > (ssize_t)0)
                p += n;
            else
            if (n != (ssize_t)-1)
                return;
            else
            if (errno != EINTR)
                return;
        }
    }
}
static void wrerrint(const int i)
{
    char          buffer[32];
    char         *p = buffer + sizeof buffer;
    unsigned int  u;

    if (i < 0)
        u = (unsigned int)(-i);
    else
        u = (unsigned int)i;

    *(--p) = '\0';
    do {
        *(--p) = '0' + (u % 10U);
        u /= 10U;
    } while (u > 0U);
    if (i < 0)
        *(--p) = '-';

    wrerr(p);
}



static void init(void) __attribute__((constructor));
static void init(void)
{
    int    argc, i, saved_errno;
    char **argv;

    saved_errno = errno;

    argv = get_argv(&argc);
    if (argv == NULL) {
        const char *const errmsg = strerror(errno);
        wrerr("libexample.so: get_argv() failed: ");
        wrerr(errmsg);
        wrerr(".\n");
        errno = saved_errno;
        return;
    }

    for (i = 0; i < argc; i++) {
        wrerr("libexample.so: argv[");
        wrerrint((int)i);
        wrerr("] = '");
        wrerr(argv[i]);
        wrerr("'\n");
    }

    free(argv);

    errno = saved_errno;
    return;
}

使用例如编译

gcc -Wall -fPIC -shared example.c -ldl -Wl,-soname,libexample.so -o libexample.so

并使用例如进行测试

LD_PRELOAD=./libexample.so /bin/echo foo bar baz baaz

(请注意,plain echo 是一个内置的 shell,您需要执行另一个二进制文件,如 /bin/echo 来加载预加载库。)

然而,大多数动态库在环境变量中取参数;例如,用于某些内存大小提示的 YOURLIB_MEM,或用于在运行时启用详细调试输出的 YOURLIB_DEBUG

(我的示例代码不使用 stdio.h 输出,因为并非所有二进制文件都使用它,特别是如果用其他语言编写的。取而代之的是 wrerr()wrerrint() 是使用低级 unistd.h I/O 直接写入标准错误的小而愚蠢的辅助函数;这总是有效的,并且在运行时产生的副作用最小。)

有问题吗?

关于c - 如何在库加载时将参数传递给构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28020711/

相关文章:

c++ - 如何在 gtk+ c 中的回调之间进行通信

linux - 如何在树莓派中设置p2p

由于多个进程导致的linux高分辨率posix定时器延迟

ruby - 更改顶部的 ruby​​ 进程名称

c++ 应用程序在非 AVX CPU 上崩溃

c++ - 从 Java 客户端最后读取套接字\n

c - 为 Matlab 中的 mex 程序提供更多内存

c++ - Lambda捕获,初始化器和嵌套结构

c - perror() 给出段。过错

c - 以固定大小的位数组作为键查找表