使用dlsym()时的转换

时间:2015-07-20 21:44:29

标签: c dlopen dlsym

我在dlsym()中使用C,我有一个问题,是dlsym()的返回值是应该显式转换还是隐式正确转换。这是功能:

double (*(compile)(void))(double x, double y)
{
    if (system("scan-build clang -fPIC -shared -g -Wall -Werror -pedantic "
           "-std=c11 -O0 -lm foo.c -o foo.so") != 0) {
        exit(EXIT_FAILURE);
    }

    void *handle;
    handle = dlopen("./foo.so", RTLD_LAZY);
    if (!handle) {
        printf("Failed to load foo.so: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    foo f;
    f = (foo)dlsym(handle, "foo");
    if (!f) {
        printf("Failed to find symbol foo in foo.so: %s\n", dlerror());
        exit(EXIT_FAILURE);
    }
    return f;
}

函数compile()不接受值并返回指向函数的指针,该函数将两个double作为输入并返回一个double。然后我设置了一个编译共享对象foo.so的系统调用。然后,我使用foo.so打开dlopen()。然后dlsym()foo中找到foo.so,并返回我在标题中定义的foo类型的对象:

typedef double (*foo)(double, double);

我是否必须投射dlsym()

2 个答案:

答案 0 :(得分:12)

编写C标准是为了假设指向不同对象类型的指针,尤其是与对象类型相对的函数指针,可能具有不同的表示形式。这就是为什么,一般来说,你不想混用指针,或者如果你做一个现代编译器会警告你,如果你想让警告静音,你通常会使用一个显式的强制转换。

另一方面,

dlsym由于它的存在假设所有指针几乎相同,因为它应该能够返回任何指向任何数据类型的指针 - 对象或< / em> function - 在你的目标文件中。

换句话说,使用dlsym的代码本质上是不可移植的,因为它不是广泛可移植的,因为它只能“移植”到那些所有指针都可以安全地互换的机器上。 (当然,这几乎是所有流行的机器。)

所以,是的,你需要投射指针以使警告静音,这样做可能会使你的代码在所有指针不相同的机器上变得不那么容易(并且警告,如果没有警告的话) ,会正确地通知您,您的代码将无效),但dlsym永远不会在这些机器上工作(或者甚至以其当前形式存在)。

(如果gcc -pedantic警告您即使是从void *到函数指针类型的显式转换,除了切换到gcc的不同版本之外,您无能为力,或者当然不要使用-pedantic。)

附录:我的回答听起来像是在指向不同类型数据的指针之间进行转换可能是一个问题,但这通常没有问题。类型void *被定义为通用数据指针:它是malloc返回的内容,并且它被定义为可以安静地转换为任何对象指针类型 - 也就是说,您甚至不需要演员。因此对于函数指针的小问题,除了之外,它几乎是dlsym的返回类型的一个很好的选择。 malloc从来没有这个问题(你几乎没有尝试过malloc指向函数的指针),而dlsym总是有这个问题(你通常试图在动态加载的对象中访问的符号)文件是至少与数据一样频繁的代码。但函数指针是void *无法保证转换为的,所以你很可能会收到警告,这就是你需要强制转换的原因,你可能会在-pedantic下得到警告,即使是管型。

答案 1 :(得分:9)

dlsym()返回void*值。该指针值可以指向对象或函数。

如果它指向一个对象,则不需要强制转换,因为C定义了从void*到任何指向对象类型的隐式转换:

int *ptr = dlsym(handle, "name");

如果它指向一个函数(可能更常见),则没有从void*到任何指向函数类型的隐式转换,因此需要强制转换。

在标准C中,不能保证void*值可以有意义地转换为函数指针。定义dlsym()的POSIX隐式保证void* 返回的dlsym()值可以有意义地转换为指向函数的类型 - 只要因为目标是相应函数的正确类型。

假设我们正在处理没有参数的void函数:

typedef void (*func_ptr_t)(void);
func_ptr_t fptr = (func_ptr_t)dlsym(handle, "name");

碰巧,gcc(-pedantic)对此发出警告:

warning: ISO C forbids conversion of object pointer to function pointer type

此警告不严格正确。 ISO C实际上禁止将对象指针转换为函数指针类型。该标准列出了一些可能不会被强制转换操作符违反的约束;将void*转换为函数指针类型不是其中之一。 C标准没有定义这种转换的行为,但由于POSIX在这种特殊情况下确实不存在问题。

对Steve Summit的回答表明:

*(void **) (&f) = dlsym(handle, "foo");

会使gcc的警告沉默。它会,但它会做出C或POSIX无法保证的假设。 POSIX保证dlsym的结果可以转换到函数指针;它并不保证它具有相同的表示或对齐。它可能有效,但我不推荐它。