C中是否有与此汇编代码类似的操作?

时间:2016-06-21 22:13:45

标签: c assembly function-pointers

今天,我在汇编代码中使用递增函数指针来创建函数的备用入口点:

.386
.MODEL FLAT, C
.DATA
    INCLUDELIB MSVCRT
    EXTRN puts:PROC
    HLO DB "Hello!", 0
    WLD DB "World!", 0
.CODE
    dentry PROC
        push offset HLO
        call puts           
        add esp, 4
        push offset WLD
        call puts
        add esp, 4
        ret
    dentry ENDP
    main PROC
        lea edx, offset dentry
        call edx
        lea edx, offset dentry
        add edx, 13
        call edx
        ret
    main ENDP
END

(我知道,从技术上讲,这段代码无效,因为它在没有初始化CRT的情况下调用puts,但它没有任何程序集或运行时错误,至少在MSVC 2010 SP1上是有效的。)

请注意,在第二次调用dentry时,我像以前一样在edx寄存器中获取了函数的地址,但是这次我在调用函数之前将它增加了13个字节。

因此该程序的输出为:

C:\Temp>dblentry
Hello!
World!
World!

C:\Temp>

Hello!\nWorld!”的第一个输出是从函数的最开始调用,而第二个输出来自从“push offset WLD”指令开始的调用。

我想知道这种事情是否存在于语言中,这些语言意味着从汇编程序(如C,Pascal或FORTRAN)升级。我知道C不会让你增加函数指针但是还有其他方法来实现这种事情吗?

2 个答案:

答案 0 :(得分:1)

您可以使用longjmp函数:http://www.cplusplus.com/reference/csetjmp/longjmp/

这是一个相当可怕的功能,但它会做你想要的。

答案 1 :(得分:1)

AFAIK你只能在asm中编写带有多个入口点的函数。

您可以在所有入口点上放置标签,这样您就可以使用普通的直接调用,而不是对第一个函数名称的偏移进行硬编码。

这样可以很容易地用C或任何其他语言来正常调用它们。

如果您担心不允许函数体重叠的工具(或人类)混乱,那么较早的入口点就像落入另一个函数体内的函数一样工作。

如果早期的入口点做了一些额外的东西,然后进入主函数,你可能会这样做。它主要是一种代码大小保存技术(可能会提高I-cache / uop-cache命中率)。

编译器倾向于在函数之间复制代码,而不是在稍微不同的函数之间共享大块的常见实现。

但是,你可以用一个额外的jmp完成它,例如:

int foo(int a) { return bigfunc(a + 1); }
int bar(int a) { return bigfunc(a + 2); }

int bigfunc(int x) { /* a lot of code */ }

查看Godbolt compiler explorer

上的真实示例

foobar尾调bigfunc,这比让bar落入bigfunc略差。 (将foo跳过bar进入bigfunc仍然很好,尤其是bar并非如此琐碎。)

跳入函数的中间通常不安全,因为非平凡函数通常需要保存/恢复某些注册表。所以序幕推动了他们,结语弹出他们。如果你跳到中间,那么序言中的pop将使堆栈失衡。 (即将返回地址弹出到寄存器中,然后返回垃圾地址)。

另见Does a function with instructions before the entry-point label cause problems for anything (linking)?