Alloca 实现 [英] Alloca implementation

查看:29
本文介绍了Alloca 实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

How does one implement alloca() using inline x86 assembler in languages like D, C, and C++? I want to create a slightly modified version of it, but first I need to know how the standard version is implemented. Reading the disassembly from compilers doesn't help because they perform so many optimizations, and I just want the canonical form.

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

I guess the hard part is that I want this to have normal function call syntax, i.e. using a naked function or something, make it look like the normal alloca().

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

Edit # 2: Ah, what the heck, you can assume that we're not omitting the frame pointer.

推荐答案

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

implementing alloca actually requires compiler assistance. A few people here are saying it's as easy as:

sub esp, <size>

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

which is unfortunately only half of the picture. Yes that would "allocate space on the stack" but there are a couple of gotchas.

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

  1. if the compiler had emitted code which references other variables relative to esp instead of ebp (typical if you compile with no frame pointer). Then those references need to be adjusted. Even with frame pointers, compilers do this sometimes.

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

more importantly, by definition, space allocated with alloca must be "freed" when the function exits.

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

The big one is point #2. Because you need the compiler to emit code to symmetrically add <size> to esp at every exit point of the function.

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

The most likely case is the compiler offers some intrinsics which allow library writers to ask the compiler for the help needed.

事实上,在 glibc(GNU 的 libc 实现)中.alloca 的实现很简单:

In fact, in glibc (GNU's implementation of libc). The implementation of alloca is simply this:

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

考虑之后,我认为编译器至少需要总是在任何使用 alloca 的函数中使用帧指针,而不管优化如何设置.这将允许通过 ebp 安全地引用所有局部变量,并且通过将帧指针恢复到 esp 来处理帧清理.

after thinking about it, the minimum I believe would be required would be for the compiler to always use a frame pointer in any functions which uses alloca, regardless of optimization settings. This would allow all locals to be referenced through ebp safely and the frame cleanup would be handled by restoring the frame pointer to esp.

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

So i did some experimenting with things like this:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

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

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

int main() {
    func();
}

不幸的是无法正常工作.通过gcc分析汇编输出后.优化似乎成为了障碍.问题似乎是,由于编译器的优化器完全不知道我的内联汇编,它习惯于以意想不到的顺序做事情并且仍然通过 esp 引用事情.

which unfortunately does not work correctly. After analyzing the assembly output by gcc. It appears that optimizations get in the way. The problem seems to be that since the compiler's optimizer is entirely unaware of my inline assembly, it has a habit of doing the things in an unexpected order and still referencing things via 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 <memset@plt>
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 <memcpy@plt>
8048496: mov    eax,DWORD PTR [ebp-0x4]
8048499: mov    DWORD PTR [esp],eax           ; <- whoops! compiler still referencing via esp
804849c: call   8048368 <puts@plt>
80484a1: leave
80484a2: ret

如您所见,事情并没有那么简单.不幸的是,我坚持我最初的主张,即您需要编译器帮助.

As you can see, it isn't so simple. Unfortunately, I stand by my original assertion that you need compiler assistance.

这篇关于Alloca 实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆