「ゼロ化」とは、「他の関数が何をしていたかわからないように、レジスターの値が0に置き換えられる」ことを意味する場合、いいえ、レジスターは使用前にゼロ化されません。ただし、GCCに情報を保存するように指示しているので、現在そこにある情報を読みたいということではなく、問題ではありません。
この情報をGCCに提供して、(ドキュメントを読んで)アセンブリコードを使い終わったときに「使用するデータが含まれるレジスタまたはメモリの場所を推測する必要がない」ようにします(たとえば、データがスタックレジスタまたは他のレジスタにあるかどうかを覚えておいてください)。
「コンパイラは...アセンブラ命令テンプレートを解析せず、それが何を意味するのか、あるいはそれが有効なアセンブラ入力であるかどうかさえも知らないため、GCCはアセンブリコードに多くの助けを必要とします。拡張asm機能はマシン命令に最もよく使用されますコンパイラ自体は存在を知りません。」
アップデート
GCCはマルチパスコンパイラとして設計されています。パスの多くは、実際にはまったく異なるプログラムです。「コンパイラ」を形成する一連のプログラムは、ソースをC、C ++、Ada、Javaなどからアセンブリコードに変換します。次に、別のプログラム(gas
GNUアセンブラの場合)がそのアセンブリコードを取得してバイナリに変換します(その後ld
、collect2
バイナリに対してさらに多くのことを実行します)。にテキストを直接渡すためのアセンブリブロックが存在しgas
、C、C ++、Ada、Javaなどの側との間で情報を渡すために必要なセットアップをコンパイラが実行できるようにclobber-list(および入力リスト)が存在します。 thegas
物事の側面、および現在レジスタにある重要な情報を、アセンブリブロックの実行前にメモリにコピーする(および後でメモリからコピーする)ことにより、アセンブリブロックから保護できることを保証します。
別の方法は、すべてのアセンブリコードブロックのすべてのレジスタを保存して復元することです。高価になる可能性のある多数のレジスタを備えたRISCマシン(たとえば、Itaniumには128個の汎用レジスタ、別の128個の浮動小数点レジスタおよび64個の1ビットレジスタがあります)。
アセンブリコードを書いてからしばらく経ちました。また、特定のレジスタを使用するよりも、GCCの名前付きレジスタ機能を使用した経験が豊富です。だから、例を見て:
#include <stdio.h>
long foo(long l)
{
long result;
asm (
"movl %[l], %[reg];"
"incl %[reg];"
: [reg] "=r" (result)
: [l] "r" (l)
);
return result;
}
int main(int argc, char** argv)
{
printf("%ld\n", foo(5L));
}
reg
アセンブリコード内で呼び出す出力レジスタを要求しました。GCCはresult
完了時に変数に自動的にコピーします。この変数にCコードとアセンブリコードで異なる名前を付ける必要はありません。私はそれが可能であることを示すためだけにそれをしました。%%eax
GCCが使用することを決定した物理レジスタ( %%ebx
、、、%%ecx
など)は、アセンブリブロックに入るときに、GCCがそのレジスタからメモリに重要なデータをコピーして、最後までそのレジスタを完全に使用できるようにします。アセンブリブロックの。
l
また、Cとアセンブリの両方で呼び出す入力レジスタを要求しました。GCCは、私が与えることを決定した物理レジスターがl
、アセンブリー・ブロックに入るときに現在C変数にある値を持つことを約束します。GCCは、アセンブリブロックに入る前に、そのレジスタにたまたま存在するデータを保護するために必要な記録管理も行います。
アセンブリコードに行を追加するとどうなりますか?言う:
"addl %[reg], %%ecx;"
GCCのコンパイラ部分はアセンブリコードをチェックしないため、のデータは保護されません%%ecx
。運%%ecx
が良ければ、GCCが%[reg]
またはに使用することを決定したレジスタの1つである可能性があります%[l]
。運が悪ければ、プログラムの他の部分で「不思議なことに」値を変更してしまいます。