我正在尝试在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");
}
答案 0 :(得分:1)
你不能。
__attribute__((constructor))
根本不支持此功能。
似乎没有任何理由可以在my_load(argc, argv)
的最开始时拨打main()
。
当程序正常退出或从main返回时,可以使用atexit
注册要调用的函数。
int main(int argc, char **argv)
{
my_load(argc, argv);
atexit(my_unload);
// ...
}
答案 1 :(得分: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
明确加载共享库时,它才有效。它在链接时直接引用,构造函数将在main中的第一条指令之前调用,并且您将始终在Argc
中找到原始值或0。
答案 2 :(得分: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
是内置的shell,您需要执行另一个二进制文件,如/bin/echo
来加载预加载库。)
但是,大多数动态库都会在环境变量中使用参数;例如,YOURLIB_MEM
用于某些内存大小提示,或YOURLIB_DEBUG
用于在运行时启用详细调试输出。
(我的示例代码不使用stdio.h输出,因为并非所有二进制文件都使用它,特别是如果用其他语言编写的话。而wrerr()
和wrerrint()
是小的愚蠢辅助函数,使用低级unistd.h I / O直接写入标准错误;这总是有效,并且在运行时产生最小的副作用。)
有问题吗?