使用dlsym()时的转换

lor*_*age 6 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;
}
Run Code Online (Sandbox Code Playgroud)

该函数compile()不接受值并返回指向函数的指针,该函数将两个doubles作为输入并返回一个double.然后我设置了一个编译共享对象的系统调用foo.so.然后我打开foo.sodlopen().然后dlsym()发现foofoo.so并返回类型的对象foo,我在报头中所定义:

typedef double (*foo)(double, double);
Run Code Online (Sandbox Code Playgroud)

我必须投dlsym()吗?

Ste*_*mit 12

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

dlsym另一方面,它的存在假定所有指针几乎相同,因为它应该能够在目标文件中返回任何指向任何数据类型(对象函数)的指针.

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

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

(并且如果gcc -pedantic警告你甚至void *对函数指针类型进行显式转换,除了切换到不同版本gcc,或者当然不使用之外,你可以做的不多-pedantic.)


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

  • 不是我的想法,而是直接从手册页中获取. (2认同)

Kei*_*son 10

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

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

int *ptr = dlsym(handle, "name");
Run Code Online (Sandbox Code Playgroud)

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

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

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

typedef void (*func_ptr_t)(void);
func_ptr_t fptr = (func_ptr_t)dlsym(handle, "name");
Run Code Online (Sandbox Code Playgroud)

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

warning: ISO C forbids conversion of object pointer to function pointer type
Run Code Online (Sandbox Code Playgroud)

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

对Steve Summit的回答发表评论表明:

*(void **) (&f) = dlsym(handle, "foo");
Run Code Online (Sandbox Code Playgroud)

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

  • 呵呵,奇怪的是,手册页将`*(void **)(&f)`列为可接受的POSIX解决方法,“它符合ISO C标准并且可以避免任何编译器警告”。感谢您提供更高的精度。 (3认同)