0

C プログラムによって呼び出される、2 つの正の数を加算するアセンブリ関数を作成する必要があります。

C プログラムは次のようになります。

#include <stdio.h>

int main(void){
int a = 0;
int b = 0;
int c = 0;
printf( "Enter first number: " );
scanf( "%d", &a );
printf( "Enter second number: " );
scanf( "%d", &b );
sum();
printf( "Answer is %d\n", sum );
}

要件は、アセンブリ関数 ( sum()) がパラメーターを渡してはならず、値を返してはならないということです。また、必要に応じて、アセンブリ関数は別のファイル sum.s にあります。

私はたくさん試し、たくさん読みました。それでも、内部の変数にアクセスできませんmain()。ご協力ありがとうございました。:)

4

4 に答える 4

3

これまでのところ、ここでの回答は、いくつかの非常に複雑でコンパイラ固有のソリューションについて語っています (実際には、コンパイルを実行するために使用されるオプションにも依存しています)。

この割り当てで探しているのは、グローバル変数を使用してパラメーターと結果を渡すことだけなのだろうか?

別の関数のローカル変数のレイアウトと、戻りアドレスの場所との関係に依存することは、かなり... クレイジーです。

于 2009-09-07T17:10:23.297 に答える
2

パラメーターを渡さないというのは、少し変わった要件のように思えます。非常に脆弱なコードになる可能性があります。通常、C コードは通常どおりパラメータを渡すようになり、アセンブリ関数は呼び出し規約で定義されている適切なレジスタやスタックからそれらを取得し、そこに結果を書き込みます。

それでも、それが本当にあなたが望むものなら、それは実現可能です。コンパイラの ABI ドキュメントを読んで、スタック フレームをどのようにレイアウトするかを理解する必要があります。次に、アセンブリ関数は、呼び出し元のスタック フレームがどこにあるかを特定する必要があります。ポインターまたはオフセットは、通常、関数が呼び出されたときにスタックにプッシュされます。したがって、ローカル変数a、b、c はメモリ内にあります。レイアウトは ABI に依存します。これらのドキュメントを読むと、呼び出し規約と、呼び出し元でスコープ内でローカルに宣言した内容にも依存することがわかります。おそらく最適化レベル。したがって、結果のアセンブリ関数は現在の実装に非常に緊密にバインドされ、脆弱になり、ほとんど何かが変更されると壊れる可能性があります。

ちなみに、printf( "Answer is %d\n",合計行では、 ではなく);を意味していると思います。sum() 関数のアドレスが生成されますが、これはおそらくコンパイラによって組み込まれ、リンク時に修正されるため、そのシンボルに実行時の結果を出力させる方法はありません。csumsum

于 2009-09-07T16:12:27.250 に答える
1

sum() を呼び出すと、コンパイラは現在の命令ポインターのアドレスを (スタックに) プッシュし、サブルーチンの場所にジャンプします。現在の命令ポインターをプッシュするのは、サブルーチンがアドレスをポップして戻ることができるようにするためです。

したがって、サブルーチン内で最初に (つまり、スタックが上から下にプッシュされるため、一番下に) あるのは戻りアドレスです。スタックのすぐ上には、サブルーチンを呼び出したルーチンのローカル変数があります。

したがって、espがスタック ポインタ レジスタであり、が指して[esp]いるメモリ内の場所であり、sp32 ビット コードを想定すると、sumサブルーチンは[esp+4]、 、[esp+8]、および[esp+12]ローカル変数のアドレスを含むアドレスを検出する必要があります。

私が言ったことを確認するために、C コードによって生成されたアセンブリを確認することをお勧めします。デバッガを使用してマシン コードを逆アセンブルするか、コンパイラ コマンド ライン オプションを使用してアセンブリ言語のリスト ファイルを生成します。 .


編集:ムーンシャドーは、これが「脆弱な」ソリューションであることについて正しいです。たとえば、' c' 変数がスタックに格納されているか、代わりにレジスタに格納されているか (または、コンパイラがハードコードされた定数ゼロであると想定するのではなく、まったく定義されているかどうか) は、コンパイラの最適化によって異なる場合があります。有効になっています。

于 2009-09-07T16:10:17.520 に答える
0

スタックがどのように構築されているかを仮定することをお勧めします。これは私がそれを描く方法です:

関数呼び出しの前:

-------
|  a  |
-------
|  b  |
-------
|  c  |
-------

関数呼び出しの後:

------- TOS
|  a  |
------- TOS - 4
|  b  |
------- TOS - 8
|  c  |
------- TOS - 12
| ret |
| eip | 
------- TOS - 16

そしてあなたは持っています:

ESP = TOS - 16

今:

mov eax, ss:[esp + 12] ; eax = a
add eax, ss:[esp +  8] ; eax += b
mov ss:[esp +  4], eax ; c   = eax
ret

トリックを行う必要があります

于 2009-09-07T16:16:09.860 に答える