gpt4 book ai didi

c - 来自 dlsym 的通用函数,带有取消引用的 float

转载 作者:太空宇宙 更新时间:2023-11-04 02:37:03 26 4
gpt4 key购买 nike

GnuCOBOL 编译器通过使用动态符号查找支持动态 CALL,但这里的 MCVE 严格是 C 语言,并且比证明(我认为的)4 字节和 8 字节大小都有效的要少一些。

这是 AMD-64,所以 sizeof *float 不等于 sizeof float。

问题仅在从 dlsym 查找中被通用(在本例中未签名)函数指针调用时取消引用 float 时才会出现。

// gcc -Wl,--export-dynamic -g -o byval byval.c -ldl

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

// hack in a 1 / 3 float 0.303030, 1050355402 as 32bit int
unsigned char field[4] = {0xca, 0x26, 0x9b, 0x3e};

// and a 1 / 6 double, 0.151515
unsigned char dtype[8] = {0x64, 0x93, 0x4d, 0x36, 0xd9, 0x64, 0xc3, 0x3f};

int aroutine(float);

int
main(int argc, char** argv)
{

float* fp = (float*)field;
double g;

void* this;
int (*calling)();

int result;

/* call the routines using generic data treated as float */
float f = *fp;
printf("Initial: %f \n", f);

printf("\nBy signature\n");
result = aroutine(*(float*)(field));

this = dlopen("", RTLD_LAZY);

printf("\nGeneric: (busted, stack gets 0x40000000)\n");
calling = dlsym(this, "aroutine");
result = calling(*(float*)(field));

printf("\nBy reference: (works when callee dereferences)\n");
calling = dlsym(this, "proutine");
result = calling((float*)(field));

printf("\nGeneric double (works):\n");
calling = dlsym(this, "droutine");
result = calling(*(double*)(dtype));

printf("\nGeneric int and double (works):\n");
calling = dlsym(this, "idroutine");
result = calling(*(int*)(field),*(double*)(dtype));

printf("\nGeneric int and float (busted) and int:\n");
calling = dlsym(this, "ifiroutine");
result = calling(*(int*)(field), *(float*)(field), *(int*)(field));

return 0;
}

int aroutine(float f) {
printf("aroutine: %f\n", f);
return 0;
}

int proutine(float *fp) {
printf("proutine: %f\n", *fp);
return 0;
}

int droutine(double g) {
printf("droutine: %g\n", g);
return 0;
}

int idroutine(int i, double g) {
printf("idroutine: %d %g\n", i, g);
return 0;
}

int ifiroutine(int i, float f, int j) {
printf("ifiroutine: %d %f %d\n", i, f, j);
return 0;
}

运行

prompt$ gcc -Wl,--export-dynamic -g -o mcve stackoverflow.c -ldl
prompt$ ./mcve
Initial: 0.303030

By signature
aroutine: 0.303030

Generic: (busted, stack gets 0x40000000)
aroutine: 2.000000

By reference: (works when callee dereferences)
proutine: 0.303030

Generic double (works):
droutine: 0.151515

Generic int and double (works):
idroutine: 1050355402 0.151515

Generic int and float (busted) and int:
ifiroutine: 1050355402 2.000000 1050355402

我想我需要了解一下 64 位 ABI 在取消引用 float 据时如何处理未签名的调用。

包含 COBOL 标签是因为当使用 FLOAT-SHORT (C float) 和 CALL BY VALUE 时,这会破坏 GnuCOBOL (生成 C 中间体),而 FLOAT- LONG (C double) CALL BY VALUE 有效,32 位整数也是如此。

顺便说一句,我很确定这不是 gcc 中的错误,因为 tcc tcc -rdynamic -g -o tccmcve stackoverflow.c -ldl 显示相同的输出, float 取消引用看起来很无聊,所以我倾向于(并希望)这是一个可以解决的问题,为编译器提供正确的语法提示,或编译时选项。

最佳答案

C99 和 C11 6.5.2.2p6 状态

If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

和 6.5.2.2p7 继续

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

因此,当您调用具有 float 参数的函数时,原型(prototype)(您称之为“签名”)告诉编译器转换floatdouble。 (类似于小于 int 的整数类型。)

显然,解决方法是使用原型(prototype)(“签名”)。如果你想传递 floatcharshort,你必须,因为没有原型(prototype),它们分别被提升为 doubleintint

然而,这真的不应该是一种负担。如果你有一些无原型(prototype)的函数指针,比如

int (*generic)() = dlsym(self, "aroutine");

如果您想调用一个原型(prototype)为 void foo(float, int, double) 的函数,您可以随时强制转换函数指针:

((void (*)(float, int, double))generic)(f_arg, i_arg, d_arg);

尽管使用具有正确原型(prototype)的临时函数指针肯定更易于阅读和维护:

{
void (*call_foo)(float, int, double) = (void *)generic;
call_foo(f_arg, i_arg, d_arg);
}

参见 POSIX dlsym() documentation以供引用。 (不再推荐 older versions 中推荐的 *(void **)(&funcptr) 习惯用法;无论如何它都很愚蠢。)

关于c - 来自 dlsym 的通用函数,带有取消引用的 float ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35976947/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com