如何通过参数构造函数库加载? [英] how to pass argument to constructor on library load?

查看:110
本文介绍了如何通过参数构造函数库加载?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Linux中创建一个共享库。如何传递参数的功能my_load()时,库加载?在我的C应用程序中,我做出test_func(通话),然后它会自动执行my_load()之前先调用的函数,那么最后它执行my_unload()

 的#include<&stdio.h中GT;无效__attribute__((构造函数))my_load(INT ARGC,CHAR *的argv []);
无效__attribute__((析构函数))my_unload(无效);
无效test_func(无效);无效my_load(INT ARGC,CHAR *的argv []){
的printf(my_load数:%d \\ n,ARGC);
}无效my_unload(无效){
的printf(my_unload \\ n);
}无效test_func(无效){
的printf(test_func()\\ n);
}


解决方案

您的动态库可以经常阅读的 的/ proc /自/ CMDLINE 来看看使用什么命令行参数执行当前可执行的。 example.c

 的#include<&stdlib.h中GT;
#包括LT&;&unistd.h中GT;
#包括LT&; SYS / types.h中>
#包括LT&;&fcntl.h GT;
#包括LT&;&string.h中GT;
#包括LT&;&errno.h中GT;静态字符** get_argv为(int * const的argcptr)
{
    焦炭** argv的;
    字符*数据= NULL;
    为size_t大小= 0; / *分配给数据* /
    为size_t用于= 0;
    为size_t ARGC,我;
    ssiz​​e_t供字节;
    INT的fd;    如果(argcptr)
        * argcptr = 0;    做{
        FD =打开(的/ proc /自/ CMDLINE,O_RDONLY | O_NOCTTY);
    }而(FD == -1放大器;&放大器;错误号== EINTR);
    如果(FD == -1)
        返回NULL;    而(1){        如果(使用> =大小){
            字符* OLD_DATA =数据;
            大小=(用来| 4095)+ 4096;
            数据= realloc的(数据,大小+ 1);
            如果(数据== NULL){
                免费(OLD_DATA);
                关闭(FD);
                错误号= ENOMEM;
                返回NULL;
            }
        }        做{
            字节=读(FD,数据+使用,大小 - 使用);
        }而(字节==(ssize_t供)-1放大器;&放大器;错误号== EINTR);
        如果(字节≤(ssize_t供)0){
            免费的(数据);
            关闭(FD);
            错误号= EIO;
            返回NULL;        }其他
        如果(字节==(ssize_t供)0)
            打破;        其他
            使用+ =字节;
    }    如果(接近(FD)){
        免费的(数据);
        错误号= EIO;
        返回NULL;
    }    / *我们是安全的,这里overallocate 1指针。 * /
    ARGC = 1;
    对于(i = 0; I<使用;我++)
        如果(数据由[i] =='\\ 0')
            ARGC ++;    / *重新分配,以容纳两个指针和数据。 * /
    ARGV = realloc的(数据,(ARGC + 1)* sizeof的(字符*)+使用+ 1);
    如果(argv的== NULL){
        免费的(数据);
        错误号= ENOMEM;
        返回NULL;
    }
    数据=(字符*)(argv的+ ARGC + 1);
    memmove与(数据,argv的,使用);    / *在情况下,输入缺少尾随NULL字节。 * /
    数据[使用] ='\\ 0';    / *分配指针。 * /
    的argv [0] =数据;
    的argc = 0;
    对于(i = 0; I<使用;我++)
        如果(数据由[i] =='\\ 0')
            的argv [++ ARGC] =数据+ I + 1;
    / *最后的指针指向过去的数据。让它结束数组。 * /
    ARGV [ARGC] = NULL;    如果(argcptr)
        * argcptr =(INT)的argc;    返回argv的;
}/ *例标准误差功能,即避免使用stdio.h中的。
* /
静态无效WRERR(为const char * P)
{
    如果(P!= NULL){
        为const char * const的Q = P + strlen的(P);
        ssiz​​e_t供N;        而(P< Q){
            N =写(STDERR_FILENO,P,(为size_t)(Q - P));
            如果(N>(ssize_t供)0)
                P + = N;
            其他
            如果(N!=(ssize_t供)-1)
                返回;
            其他
            如果(错误!= EINTR)
                返回;
        }
    }
}
静态无效wrerrint(co​​nst int的我)
{
    字符缓冲区[32];
    的char * p =缓冲区+ sizeof的缓冲;
    unsigned int类型U;    如果(ⅰ℃,)
        U =(无符号整数)( - 我);
    其他
        U =(无符号整数)I;    *( - P)='\\ 0';
    做{
        *( - P)='0'+(U%10U);
        U / = 10U;
    }而(U> 0U);
    如果(ⅰ℃,)
        *( - P)=' - ';    WRERR(P);
}静态无效的init(无效)__attribute __((构造函数));
静态无效的init(无效)
{
    INT ARGC,我,saved_errno;
    焦炭** argv的;    saved_errno =错误号;    ARGV = get_argv(安培; ARGC);
    如果(argv的== NULL){
        为const char * const的ERRMSG =字符串错误(错误);
        WRERR(libexample.so:get_argv()失败:);
        WRERR(ERRMSG);
        WRERR(\\ n);
        错误号= saved_errno;
        返回;
    }    对于(i = 0; I< ARGC,我++){
        WRERR(libexample.so:的argv [);
        wrerrint((INT)一);
        WRERR(] =');
        WRERR(的argv [I]);
        WRERR('\\ n);
    }    免费(ARGV);    错误号= saved_errno;
    返回;
}

编译使用例如

 的gcc -Wall -shared -fPIC example.c -ldl轮候册,-soname,libexample.so -o libexample.so

和使用测试例如

  LD_ preLOAD = / libexample.so /斌/回声富酒吧巴兹•巴兹

(注意,普通的回声是内置了一个shell,你需要执行像另一个二进制/斌/回声加载preLOAD库)。

不过,最有活力的图书馆采取的环境变量,而不是论据;例如, YO​​URLIB_MEM 对于一些内存大小暗示,或 YO​​URLIB_DEBUG 为运行时启用详细的调试输出。

(我的例子code不使用stdio.h中输出,因为不是所有的可执行文件需要使用它,特别是如果写在其他语言。相反, WRERR() wrerrint()是使用低级别的unistd.h中的I / O直接写入到标准错误愚蠢的小辅助功能,这始终工作,并使副作用最小效果在运行时)。

问题?

I am trying to create a shared library in Linux. How can I pass an argument to function my_load() when library is loaded? In my C application, I make a call to test_func() then it automatically executes my_load() first before the called function then lastly it executes 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");
}

解决方案

Your dynamic library can always read /proc/self/cmdline to see what the command-line parameters used to execute the current executable are. 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;
}

Compile using e.g.

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

and test using e.g.

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

(Note that plain echo is a shell built-in, and you need to execute another binary like /bin/echo to load the preload library.)

However, most dynamic libraries take arguments in environment variables instead; for example, YOURLIB_MEM for some memory size hint, or YOURLIB_DEBUG for enabling verbose debugging output during runtime.

(My example code does not use stdio.h output, because not all binaries use it, especially if written in some other language. Instead, the wrerr() and wrerrint() are small stupid helper functions that use low-level unistd.h I/O to write directly to standard error; this always works, and causes minimal side effects at run time.)

Questions?

这篇关于如何通过参数构造函数库加载?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆