当前位置:  开发笔记 > 编程语言 > 正文

Alloca实施

如何解决《Alloca实施》经验,为你挑选了2个好方法。

如何在D,C和C++等语言中使用内联x86汇编程序实现alloca()?我想创建一个稍微修改过的版本,但首先我需要知道标准版本是如何实现的.从编译器中读取反汇编并没有帮助,因为它们执行了很多优化,我只想要规范形式.

编辑:我想困难的部分是我希望它具有正常的函数调用语法,即使用裸函数或其他东西,使它看起来像普通的alloca().

编辑#2:啊,到底是什么,你可以假设我们没有省略帧指针.



1> Evan Teran..:

实现alloca实际需要编译器帮助.这里的一些人说这很简单:

sub esp, 

不幸的是,这只是图片的一半.是的,这将"在堆栈上分配空间",但有几个陷阱.

    如果编译器发出的代码引用了相对于其他变量esp而不是ebp (典型的话,如果你没有使用帧指针进行编译).然后需要调整这些参考.即使使用帧指针,编译器有时也会这样做.

    更重要的是,根据定义,alloca当函数退出时,必须"释放" 分配的空间.

最重要的是第2点.因为您需要编译器发出代码以对称地添加esp函数的每个出口点.

最可能的情况是编译器提供了一些内在函数,允许库编写者向编译器询问所需的帮助.

编辑:

实际上,在glibc(GNU的libc实现)中.实施alloca简单就是这样:

#ifdef  __GNUC__
# define __alloca(size) __builtin_alloca (size)
#endif /* GCC.  */

编辑:

在考虑它之后,我相信所需的最小值将是编译器在任何使用的函数中始终使用帧指针alloca,而不管优化设置如何.这将允许所有本地人ebp安全地被引用,并且帧清理将通过恢复帧指针来处理esp.

编辑:

所以我做了一些像这样的事情的实验:

#include 
#include 
#include 

#define __alloca(p, N) \
    do { \
        __asm__ __volatile__( \
        "sub %1, %%esp \n" \
        "mov %%esp, %0  \n" \
         : "=m"(p) \
         : "i"(N) \
         : "esp"); \
    } while(0)

int func() {
    char *p;
    __alloca(p, 100);
    memset(p, 0, 100);
    strcpy(p, "hello world\n");
    printf("%s\n", p);
}

int main() {
    func();
}

遗憾的是,它无法正常工作.在通过gcc分析装配输出之后.似乎优化会妨碍.问题似乎是因为编译器的优化器完全没有意识到我的内联汇编,所以它习惯于以意想不到的顺序执行操作并仍然通过引用来处理事物esp.

这是最终的ASM:

8048454: push   ebp
8048455: mov    ebp,esp
8048457: sub    esp,0x28
804845a: sub    esp,0x64                      ; <- this and the line below are our "alloc"
804845d: mov    DWORD PTR [ebp-0x4],esp
8048460: mov    eax,DWORD PTR [ebp-0x4]
8048463: mov    DWORD PTR [esp+0x8],0x64      ; <- whoops! compiler still referencing via esp
804846b: mov    DWORD PTR [esp+0x4],0x0       ; <- whoops! compiler still referencing via esp
8048473: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp           
8048476: call   8048338 
804847b: mov    eax,DWORD PTR [ebp-0x4]
804847e: mov    DWORD PTR [esp+0x8],0xd       ; <- whoops! compiler still referencing via esp
8048486: mov    DWORD PTR [esp+0x4],0x80485a8 ; <- whoops! compiler still referencing via esp
804848e: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp
8048491: call   8048358 
8048496: mov    eax,DWORD PTR [ebp-0x4]
8048499: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp
804849c: call   8048368 
80484a1: leave
80484a2: ret

如你所见,它并非如此简单.不幸的是,我坚持我最初的断言,你需要编译器帮助.



2> Michael Burr..:

这样做很棘手 - 事实上,除非你对编译器的代码生成有足够的控制权,否则它不能完全安全地完成.你的例程必须操纵堆栈,这样当它返回时,所有东西都被清理了,但是堆栈指针仍然处于这样一个位置,即内存块保留在那个位置.

问题是,除非你可以通知编译器已经在函数调用中修改了堆栈指针,它可能会决定它可以继续通过堆栈指针引用其他本地(或其他) - 但偏移将是不正确.

推荐阅读
手机用户2502851955
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有