Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

c - how to pass argument to constructor on library load?

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
", argc);
}

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

void test_func(void) {
printf("test_func()
");
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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] == '')
            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] = '';

    /* Assign the pointers. */
    argv[0] = data;
    argc = 0;
    for (i = 0; i < used; i++)
        if (data[i] == '')
            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) = '';
    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(".
");
        errno = saved_errno;
        return;
    }

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

    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?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...