編集:マイケルがコメントで指摘しているように、以下に書かれていることは、最適化コンパイラによるスタック相対アドレス指定のため、実際には機能しません。したがって、本番レベルalloca
では、実際に「動作」するためにコンパイラの助けが必要です。alloca
しかし、うまくいけば、以下のコードは、内部で何が起こっているのか、そして心配するスタック相対アドレス指定の最適化がなかった場合にどのような関数が機能したかについてのいくつかのアイデアを与えることができます。
ところで、自分で簡単なバージョンを作成する方法に興味があった場合に備えalloca
て、その関数は基本的にスタックに割り当てられたスペースへのポインタを返すため、スタックを適切に操作できる関数をアセンブリに記述できます。呼び出し元の現在のスコープで使用できるポインターを返します(呼び出し元が戻ると、呼び出し元alloca
からの戻りがスタックをクリーンアップするため、このバージョンのスタックスペースポインターは無効になります)。
Unix 64ビットABIを使用するx86_64プラットフォームでLinuxのフレーバーを使用していると仮定して、「my_alloca.s」というファイル内に以下を配置します。
.section .text
.global my_alloca
my_alloca:
movq (%rsp), %r11 # save the return address in temp register
subq %rdi, %rsp # allocate space on stack from first argument
movq $0x10, %rax
negq %rax
andq %rax, %rsp # align the stack to 16-byte boundary
movq %rsp, %rax # save address in return register
pushq %r11 # push return address on stack
ret # return back to caller
次に、C / C ++コードモジュール(つまり、「。cpp」ファイル)内で、次のように使用できます。
extern my_alloca(unsigned int size);
void function()
{
void* stack_allocation = my_alloca(BUFFERSIZE);
//...do something with the allocated space
return; //WARNING: stack_allocation will be invalid after return
}
を使用して「my_alloca.s」をコンパイルできますgcc -c my_alloca.s
。これにより、「my_alloca.o」という名前のファイルが作成されます。このファイルを使用して、を使用gcc -o
または使用して他のオブジェクトファイルとリンクできますld
。
この実装で私が考えることができる主な「落とし穴」は、アクティベーションレコードとスタックベースポインタ(つまり、RBP
x86_64のポインタ)ですが、関数呼び出しごとに明示的に割り当てられたメモリです。次に、コンパイラはスタックに割り当てられたメモリを認識しないため、呼び出し元のリターンでスタックをクリーンアップし、プッシュされた呼び出し元のリターンアドレスであると信じているものを使用してジャンプバックしようとします。関数呼び出しの開始時にスタック上で、no-wheres-villeを指している命令ポインターにジャンプし、バスエラーまたはある種のアクセスエラーでクラッシュする可能性があります。許可されていないメモリ位置でコードを実行します。
コンパイラがスタックスペースを使用して引数を割り当てる場合(引数が1つしかないため、Unix 64ビットABIごとにこの関数を使用するべきではありません)など、実際には他にも危険なことが発生する可能性があります。関数呼び出しの直後のスタックのクリーンアップ。ポインターの有効性を台無しにします。しかしexecvp()
、エラーが発生しない限り戻らないのような関数を使用すれば、これはそれほど問題にはならないはずです。
全体として、このような機能はプラットフォームに依存します。