2

HP-UXで逆アセンブルされたPhrackの記事を読んでいたところです。HP-UXとSPARCで可能な2つのクラスの関数があることを読みました。リーフおよび非リーフ関数。以下は私がここから取った分解のセクションです。

(gdb) disass leaf
Dump of assembler code for function foo:
0x3280 <leaf>:           copy r3,r1
0x3284 <leaf+4>:         copy sp,r3
0x3288 <leaf+8>:         stw,ma r1,40(sr0,sp)
0x328c <leaf+12>:        stw r26,-24(sr0,r3)
0x3290 <leaf+16>:        stw  r0,8(sr0,r3)
0x3294 <leaf+20>:        ldi 1,r19
0x3298 <leaf+24>:        stw  r19,8(sr0,r3)
0x329c <leaf+28>:        ldo 40(r3),sp
0x32a0 <leaf+32>:        ldw,mb -40(sr0,sp),r3
0x32a4 <leaf+36>:        bv,n r0(rp)
End of assembler dump.
(gdb)

通常、関数が呼び出されると、関数の実行が終了すると、プログラムが制御をどこに戻すかをプログラムが認識できるように、リターンアドレスがスタックにプッシュされます。これらの葉の機能の場合、それはどのように機能しますか?

私はHP-UX/SPARCマシンにアクセスできないので、これを自分で試す方法はありません(同じ理由で、この場合のアセンブリもよくわかりません)。

この場合、制御が呼び出し先関数にどのように戻るかを誰かが説明できますか?

4

1 に答える 1

4

まず、リンク先のドキュメントと表示するコードは、Sparcではなく、別個のアーキテクチャであるPA-RISCです。私の知る限り、Sparcベースのシステムで動作するHP/UXのバージョンはありません。

それにもかかわらず、リーフ機能に関するポイントは、Sparc、PA-RISC、PowerPC、ARM、MIPSなどの多くのアーキテクチャで類似しています...実際にはすべてのRISCアーキテクチャです。これらすべてについて、関数呼び出しを実行するオペコードは、リターンアドレスをスタックに格納しません。実際、ハードウェアで知られているような「実際の」スタックはありません。代わりに、特定のレジスタをスタックポインタとして使用するためのソフトウェア規則があります。呼び出し元のオペコードは、返されたアドレスを特定のレジスタ(通常は「リンクレジスタ」と呼ばれます)に格納します。関数から戻るためのオペコードは、そのレジスタを読み取るだけです。

関数自体(Aと呼びましょう)が別の関数(B)を呼び出す場合、そのネストされた呼び出しもリンクレジスタを使用します。ただし、Aは、戻るときにリンクレジスタの内容を必要とします。したがって、Aはリンクレジスタをどこかに、通常はメモリに、より正確には「スタック」として従来使用されていたメモリ領域に保存する必要があります。

リーフ関数は、他の関数を呼び出さない関数です。それはただその仕事をして戻ってきます。リンクレジスタの内容を変更するものは何もないため、リーフ関数はリンクレジスタをスタックに保存する必要はありません。

これも従来型の追加の制約は、スタックフレーム構造に関するものです。一部のオペレーティングシステム、一部のアーキテクチャでは、非常に特殊な方法(「スタックフレーム」と呼ばれる)でスタックを使用する機能を要求します。これは、デバッガーによって確実に探索できます。次に、仕様では、スタック上の正確なスロットにリターンアドレスを保存する必要があり、呼び出された関数は、実行の非常に早い段階でそれを実行する必要があります。次に、同じオペレーティングシステムは、これがリーフ関数に絶対に必要ではないと述べる場合があります(その関数が他の関数を呼び出さない場合、つまり、スタック上にフレームがプッシュされていない場合、デバッガーはフレームのない関数を処理する方が簡単です。欠落しているもの)。スタックフレームを設定しない方が、スタックフレームを設定するよりも効率的です(実行速度とコードサイズの点で)。したがって、例外です。

于 2011-03-30T21:39:46.230 に答える