2

在 Mitchell 的书(Concepts in Programming Languages)的第 6.2.1 章中,它提到:

类型铸件。类型转换允许将一种类型的值用作另一种类型。特别是在 C 中,可以将整数强制转换为函数,从而允许跳转到不包含正确形式的指令的位置成为 C 函数。

所以我打算使用这种非安全性并做一些不寻常的事情我尝试了这样的事情(伪代码):

int x = 0;
print "loop";
x();

创建一个无限循环。我尝试通过更改和测试,但我无法应对。我怎么能像这些东西一样做一些事情或其他事情?

提前致谢

4

4 回答 4

6

这不是它的工作原理。

在 C 中,您可以将整数值转换为函数指针值,然后调用这样的函数指针

void (*ptr)() = (void (*)())42;
ptr();

但这可能只会导致崩溃,除非您确切地知道自己在做什么,即您已经以某种方式知道在地址 42 处启动具有该签名的函数;在系统编程中可能会出现像本例中这样的固定地址,而在应用程序编程中则极为罕见。

实际发生的比预期更频繁的事情(尤其是在 Windows 编程中)是传递转换为某些整数的函数指针(在 / 任何人中的回调?LPARAMWPARAM,然后将它们转换回函数指针以实际使用它们。

此外,执行此类转换/调用时会发生什么超出了 C 标准的范围,这使得实现可以自由地在这方面做他们想做的事情。

于 2012-12-30T14:57:18.877 回答
3

@Sorush,这是一个示例,可以帮助您更好地了解幕后发生的事情:

#include <stdio.h>

int main(void)
{
    printf("begin\n");
    printf("loop\n");

    // declare a function pointer
    int (*loopPtr)();
    // set the function pointer to the current function
    loopPtr = main;
    // skip over the first printf();
    loopPtr += 22;
    // call the new location
    loopPtr();
}

对我来说,它在编译时可以在 x86_64 上运行clang -O0(嗯,它可以一直工作到堆栈耗尽,因为这是无限递归,并且每个函数调用都会遍历堆栈空间)。

我通过编译确定了偏移量 22,然后反汇编并main()从 second 的地址中减去 start 的地址printf()

首先,我编译它:

clang -O0 test.c

然后拆开看:

otool -tv a.out

...产生了这个输出:

[...]
_main:
0000000100000ee0    pushq   %rbp
0000000100000ee1    movq    %rsp,%rbp
0000000100000ee4    subq    $0x20,%rsp
0000000100000ee8    leaq    0x00000073(%rip),%rdi
0000000100000eef    movb    $0x00,%al
0000000100000ef1    callq   0x100000f40
0000000100000ef6    leaq    0x0000006c(%rip),%rdi
0000000100000efd    movl    %eax,0xf4(%rbp)
0000000100000f00    movb    $0x00,%al
0000000100000f02    callq   0x100000f40
[...]

_main:表示main()函数的入口点,其首地址为0x100000ee0。第一callq条指令对应于第一个printf()调用,我想跳过它,所以我选择了紧随其后的地址:0x100000ef6。0x100000ef6 减去 0x100000ee0 是十进制的 22。

于 2012-12-31T15:11:21.903 回答
2

好吧,x()不会神奇地调用该地址的函数。我认为他的意思是:

typedef void (*functionPtr)();

int x;
//...
functionPtr foo = (functionPtr)x;
//or
functionPtr goo = (functionPtr)&x;
foo();
于 2012-12-30T14:57:40.433 回答
1

你的书有误导性。标准允许的唯一事情是强制转换操作,并且只有在值合适的情况下才会这样做。在大多数情况下,执行这样的函数指针是未定义的行为。你必须非常了解你的系统才能做到这一点。所以你引用段落的第二部分:

特别是在 C 中,可以将整数强制转换为函数,从而允许跳转到不包含正确形式的指令的位置成为 C 函数。

在那种形式下是不正确的。特别是所有体面的现代系统都不允许您执行数据,您必须为页面设置特殊标志,以便可以认为它包含可执行代码。

于 2012-12-30T15:08:18.137 回答