从动态库调用函数?

Calling function from dynamic library?

我正在 Linux 上尝试使用动态库和 C。以下代码将打印错误的输出:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **arg)
{
        void *dl = dlopen("./lib.so", RTLD_NOW);
        if (!dl) {
                fprintf(stderr, "ERROR: %s\n", dlerror());
                exit(1);
        }

        char *ver = dlsym(dl, "show_version");
        printf("%s\n", ver);
}

如果我进行以下更改,输出将是正确的:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

int main(int argc, char **arg)
{
        void *dl = dlopen("./lib.so", RTLD_NOW);
        if (!dl) {
                fprintf(stderr, "ERROR: %s\n", dlerror());
                exit(1);
        }

        char *(*ver)() = dlsym(dl, "show_version");
        printf("%s\n", ver());
}

我不确定 char *(*ver)() 在做什么以及为什么需要它?谁能解释一下?

dlsym - 获取共享对象或可执行文件中符号的地址

这意味着当您执行 dlsym(dl, "show_version"); 时,您实际上并没有调用共享库中的函数 show_version。您获得该函数的地址 - 可用于反复调用该函数。

要“解码”char *(*ver)() 的含义,您可以使用通常所说的 Clockwise/Spiral Rule

        +-----+
        |     V
char*  (*ver) ()   ver is a 
 ^      ^ |   |    pointer to
 |      | |   |    a function (taking no arguments)
 |      +-+   |    returning char*
 |            |
 +------------+

我假设以上内容与您放入共享库中的 show_version 函数的签名匹配。示例:

// a function (taking no arguments), returning a char*
char *show_version(void) {
    static char version[] = "1.0";
    return version;
}

第一次尝试使用相同的规则,char* ver:

char* ver
  ^    |     ver is a
  |    |     char*
  +----+

您需要一个指向函数的指针(具有正确的签名)才能调用该函数并获得您想要的结果。您不能调用 char* ,当您调用 printf("%s\n", ver); 时,它只会开始读取地址(存储函数的位置)的内存,直到找到空终止符。你可能看到的只是乱码。

另一方面,如果你有一个合适的函数指针,你可以像你注意到的那样,用 ver() 调用它指向的函数,你会在 [=45 中得到一个 char* =] 指向动态加载函数的字符串 returned.

您还可以在不涉及共享库的情况下在程序中使用函数指针。

#include <stdio.h>

long foo(short x, int y) {
    return x + y;
}

int main() {
    long(*foo_ptr)(short, int) = foo;

    // foo_ptr is a pointer to a function taking (short, int) as
    // arguments and returning a long

    printf("%ld\n", foo(1, 2) );       // prints 3
    printf("%ld\n", foo_ptr(1, 2) );   // also prints 3
}

dlsym(dl, "show_version") returns 符号 show_version 的地址。因为show_version是一个函数,所以就是函数的地址。

char *ver = dlsym(…);把那个指针放在一个char *里面,基本没什么用。指向函数的指针不指向对打印有用的字节。然后 printf("%s\n", ver); 说要打印 ver 指向的字节,就好像它们是一个字符串一样。但是字节有(在典型的 C 实现中)函数的机器代码。它们不是您要打印的字符串的字节。

char *(*ver)() = dlsym(…);ver 定义为指向未指定参数且 returns 为 char * 的函数的指针。要查看此内容:

  • char <i>something</i> 声明 <i>something</i> 到成为 char.
  • char *<i>something</i> 声明 <i>something</i>成为指向 char.
  • 的指针
  • char *<i>something</i>() 声明 <i>something</i> 成为未指定参数的函数 returns 指向 char.
  • 的指针
  • char *(*<i>something</i>)() 声明 <i>something</i> 指向此类函数的指针。

然后,在printf("%s\n", ver());ver()调用这个函数。 char * 它 returns 传递给 printf 进行打印。