2015-01-19 2 views
0

Я пытаюсь создать общую библиотеку в 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"); 
} 

ответ

1

Вы не можете.

__attribute__((constructor)) просто не поддерживает это.

Похоже, что вы не можете просто позвонить по номеру my_load(argc, argv) в самом начале main().

Вы можете использовать функцию atexit, чтобы зарегистрировать функцию, которая будет вызываться, когда ваша программа выходит из системы или возвращается с основного.

int main(int argc, char **argv) 
{ 
    my_load(argc, argv); 
    atexit(my_unload); 

    // ... 
} 
1

AFAIK, невозможно передать аргументы функции gcc-конструктора и деструктора. Лучшее, что вы можете сделать, это использовать глобальные переменные.

В вас Например, вы могли бы попробовать:

В основной:

int Argc; 
char *Argv[]; 

int main(int argc, char *argv[]) { 
    Argc = argc; 
    Argv = argv; 
    ... 
} 

В общей библиотеке:

extern int Argc; 
... 
void __attribute__ ((constructor)) my_load(); 
... 
void my_load() { 
printf("my_load: %d\n", Argc); 
} 

Но в любом случае, он может работать только если вы явно загрузить общий библиотека через dlopen. Он напрямую ссылается на время ссылки, функция конструктора будет вызываться перед первой инструкцией в главном, и вы всегда найдете исходное значение или 0 в Argc.

1

Ваша динамическая библиотека всегда может читать /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 

(Обратите внимание, что простой echo является оболочкой встроенный, и вы должны выполнить еще один двоичный файл, как /bin/echo загрузить библиотеку преднагрузки.)

Однако наиболее динамические библиотеки принимают аргументы в переменных среды вместо ; например, YOURLIB_MEM для подсказки размера памяти или YOURLIB_DEBUG для включения подробного вывода отладки во время выполнения.

(Мой пример кода не использует stdio.h выход, потому что не все бинарные файлы использовать, особенно если написано в каком-то другом языке. Вместо этого, wrerr() и wrerrint() маленькие глупые вспомогательные функции, которые используют низкоуровневые unistd.h I/O для прямой записи в стандартную ошибку, это всегда работает и вызывает минимальные побочные эффекты во время выполнения.)

Вопросы?

+1

Это, конечно, предполагает, что '/ proc' установлен, что не всегда так. –

+1

@ JonathonReinhart: Правда. Однако вы не можете предположить, что программа будет работать без '/ proc', установленного в Linux в любом случае. Вы теряете немного функциональности без '/ proc', включая некоторые функции glibc, такие как [' fexecve() '] (http://man7.org/linux/man-pages/man3/fexecve.3.html) и такие утилиты, как 'pidof', перестают работать. Это не так важно, хотя: как я уже сказал, * «большинство динамических библиотек [должны] принимать аргументы в переменных среды» *, и это никак не зависит от '/ proc'. –

Смежные вопросы