2016-06-21 9 views
1

今日、私は関数に別のエントリ・ポイントを作成するために、アセンブリコードで関数ポインタをインクリメントして遊ん:(私が知っている、それはCRTが初期化されずにputsを呼び出しますので、技術的には、このコードは無効ですこのアセンブリコードに類似したC言語での操作はありますか?

.386 
.MODEL FLAT, C 
.DATA 
    INCLUDELIB MSVCRT 
    EXTRN puts:PROC 
    HLO DB "Hello!", 0 
    WLD DB "World!", 0 
.CODE 
    dentry PROC 
     push offset HLO 
     call puts   
     add esp, 4 
     push offset WLD 
     call puts 
     add esp, 4 
     ret 
    dentry ENDP 
    main PROC 
     lea edx, offset dentry 
     call edx 
     lea edx, offset dentry 
     add edx, 13 
     call edx 
     ret 
    main ENDP 
END 

が、これは少なくともアセンブリやランタイムエラーがなくても動作します)。

dentryへの2回目の呼び出しでは、以前のようにedxレジスタの関数のアドレスを取得しましたが、今回はインクリメントしましたその関数を呼び出す前に13バイト分。

このプログラムの出力は、したがってある:

C:\Temp>dblentry 
Hello! 
World! 
World! 

C:\Temp> 

第二出力が始まるコールからのものであるのに対し、「Hello!\nWorld!」の最初の出力は、関数の先頭への呼び出しです " push offset WLD "命令である。

C、Pascal、FORTRANなどのアセンブラからステップアップすることを意図した言語でこの種のものが存在するのだろうかと思います。私はCが関数ポインタを増やすことはできませんが、このようなことを達成するための他の方法があるのは分かりますか?

+4

参照してください。より高水準の言語(さらにはその点ではasm)では、2つの別々の関数に分割し、一方を他方の関数と呼び、コンパイラに最適な実装を選択させる必要があります。 – Jester

+1

私は以前のFORTRANが関数の複数のエントリーポイントをサポートしていると信じていました。すばやいgoogle検索ではhttps://gcc.gnu.org/onlinedocs/gcc-3.4.4/g77/Alternate-Entry-Points.htmlとhttps:/ /docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html – ninjalj

+0

実際の質問は、どうしてこのようにしたいのですか?これはメンテナンスの悪夢です。最適化まで:プログラムの99%以上がこれを必要としません。残りの部分は、コンパイラや70/80ies BASICのハッキングよりはるかに良い構造で最適化することができます。 – Olaf

答えて

1

あなたは、longjmp関数を使用することができます。http://www.cplusplus.com/reference/csetjmp/longjmp/

それはかなり恐ろしい機能だが、それはあなたが求めるものをやります。

+0

longjmpでは、親関数内のポイントに戻ることができます。すなわち、現在実行中の機能の親ではない任意の機能にではなく、コールツリーをバックアップする。それはクロスファンクションジャンプを行う別の方法ですが、使用要件が非常に異なるため、OPが求めているものとはまったく同じものではありません。 –

+0

はい、longjmpには、ジャンプエントリの事前実行が必要です。しかし、OPサンプルコードもそうです。 – Illishar

+1

いいえ、それはありません! OPの 'main()'は '(dentry + 13)()'に '呼び出し 'を行います。これは 'dentry()'(これはとにかく2回目の '呼び出し'の前に返された)への最初の '呼び出し 'に関係なく同じ方法で起こります。それは類似していません。 –

1

AFAIKでは、複数のエントリポイントを持つ関数をasmで書くことしかできません。

すべてのエントリポイントにラベルを付けることができるので、最初のfunction-nameからのオフセットをハードコードする代わりに、通常のダイレクトコールを使用できます。

これにより、通常Cや他の言語からの呼び出しが容易になります。

以前のエントリポイントは、関数本体が重なり合わないようにするツール(または人間)が混乱することを心配している場合、別の関数の本体にフォールスルーする関数のように機能します。


初期エントリーポイントが少しの余分な処理をしてメイン機能に入る場合は、これを行うことができます。主にコードサイズの節約手法(I-cache/uop-cacheヒット率を向上させる可能性があります)になります。


コンパイラは、わずかに異なる機能間で大きな共通の実装を共有するのではなく、関数間でコードを複製する傾向があります。持つよりも少し悪いですGodbolt compiler explorer

foobar tailcall bigfunc、上の実際の例を参照してください

int foo(int a) { return bigfunc(a + 1); } 
int bar(int a) { return bigfunc(a + 2); } 

int bigfunc(int x) { /* a lot of code */ } 

しかし、あなたはおそらくのようなものを一つだけ余分jmpでそれを達成することができますbarbigfuncにフォールスルー。(bigfuncbarオーバーfooジャンプを持つことは、ESP、まだ良いです。barはその簡単ではない場合。)


関数の途中にジャンプされていない一般的に安全で、通常、非自明な機能なぜならいくつかのregsを保存/復元する必要があります。プロローグがそれらをプッシュし、エピローグがそれらをポップします。途中でジャンプすると、プロローグのpopがスタックのアンバランスを起こします。 (すなわち、戻りアドレスをレジスタにポップ・オフし、ガベージ・アドレスに戻る)。

は、それは一般的にあらゆる種類の問題につながる関数プロローグをスキップするので動作しません。また、​​

関連する問題