52

Linux x86-64 ABI がレジスタとスタックを使用してパラメーターを関数に渡す方法を理解していると思います (前の ABI の説明を参照)。私が混乱しているのは、関数呼び出し全体で保持されると予想されるかどうか/どのレジスターかです。つまり、どのレジスターが破壊されないことが保証されているのでしょうか?

4

3 に答える 3

4

ABI は、標準準拠のソフトウェアが何を期待できるかを指定します。主に、コンパイラ、リンカー、およびその他の言語処理ソフトウェアの作成者向けに書かれています。これらの作成者は、同じ (または異なる) コンパイラによってコンパイルされたコードで適切に動作するコードをコンパイラが生成することを望んでいます。それらはすべて一連のルールに同意する必要があります: 関数への仮引数が呼び出し元から呼び出し先に渡される方法、関数の戻り値が呼び出し先から呼び出し元に返される方法、呼び出し境界を越えて保持/スクラッチ/未定義のレジスターなどの上。

たとえば、ある規則では、関数用に生成されたアセンブリ コードは値を変更する前に保存レジスタの値を保存する必要があり、コードは呼び出し元に戻る前に保存された値を復元する必要があると述べています。スクラッチ レジスタの場合、レジスタ値を保存および復元するために生成されたコードは必要ありません。必要に応じてそうすることができますが、標準準拠のソフトウェアがこの動作に依存することは許可されていません (依存する場合、標準準拠のソフトウェアではありません)。

アセンブリ コードを作成している場合、これらと同じ規則に従う責任があります (コンパイラの役割を果たしていることになります)。つまり、コードが呼び出し先保存レジスタを変更する場合、元のレジスタ値を保存および復元する命令を挿入する必要があります。アセンブリ コードが外部関数を呼び出す場合、コードは標準に準拠した方法で引数を渡す必要があります。これは、呼び出し先が戻ったときに保持されているレジスタ値が実際に保持されているという事実に依存する可能性があります。

ルールは、標準に準拠したソフトウェアがどのようにうまくやっていくかを定義します。ただし、これらの規則に従わないコードを作成 (または生成) することは完全に合法です! コンパイラは、特定の状況下ではルールに従う必要がないことを知っているため、常にこれを行います。

たとえば、foo という名前の C 関数が次のように宣言されていて、そのアドレスが取得されていないとします。

static foo(int x);

コンパイル時に、コンパイラは、この関数が現在コンパイル中のファイル内の他のコードによってのみ呼び出されることを 100% 確信しています。関数fooは、静的であることの定義を考えると、他のものから呼び出すことはできません。コンパイラはコンパイル時にのすべての呼び出し元を認識しているため、コンパイラfooは必要な呼び出しシーケンスを自由に使用できます (呼び出しをまったく行わないこと、つまり のコードをfooの呼び出し元にインライン化することまで) foo

アセンブリ コードの作成者は、これも実行できます。つまり、その合意が標準準拠ソフトウェアの期待に干渉したり違反したりしない限り、2 つ以上のルーチン間で「非公開合意」を実装できます。

于 2013-08-02T22:01:13.050 に答える