alloca
実際に実装するには、コンパイラの支援が必要です。ここにいる数人は、次のように簡単だと言っています。
sub esp, <size>
残念ながら、これは全体像の半分にすぎません。はい、それは「スタックにスペースを割り当てる」ことになりますが、いくつかの問題があります。
コンパイラがesp
代わりに関連する他の変数を参照するコードを発行したebp
場合 (フレームポインタなしでコンパイルする場合に典型的)。次に、それらの参照を調整する必要があります。フレームポインターを使用しても、コンパイラーはこれを行うことがあります。
さらに重要なことは、定義により、関数が終了するときに割り当てられたスペースをalloca
「解放」する必要があることです。
大きなものはポイント#2です。関数のすべての終了ポイントで対称的に追加するコードを発行するコンパイラが必要なためです。<size>
esp
最も可能性の高いケースは、ライブラリの作成者がコンパイラに必要なヘルプを求めることができる組み込み関数をコンパイラが提供していることです。
編集:
実際、glibc (GNU による libc の実装) では。の実装は次のalloca
とおりです。
#ifdef __GNUC__
# define __alloca(size) __builtin_alloca (size)
#endif /* GCC. */
編集:
それについて考えた後、最適化設定に関係なく、コンパイラが を使用するすべての関数でフレームポインタを常に使用することが最低限必要であると私は信じています。alloca
これにより、すべてのローカルをebp
安全に参照できるようになり、フレーム ポインタを に復元することでフレームのクリーンアップが処理されますesp
。
編集:
だから私はこのようなことでいくつかの実験をしました:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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 <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
ご覧のとおり、それほど単純ではありません。残念ながら、コンパイラの支援が必要だという最初の主張を支持します。