C ++で「不明な」名前で関数を呼び出す。 パート1-cdecl

問題の声明



「不明な」関数名を書いたとき、どういう意味でしたか? これは、関数の名前、そのパラメーター、そして最終的には呼び出し規約が、プログラムの実行中にのみ知られることを意味します。 彼女に挑戦しましょう! =)



次に、cdecl標準に従って関数を呼び出そうとします。

ウィキペディアの抜粋:

cdecl呼び出し規約は、x86アーキテクチャの多くのCシステムで使用されています。 cdeclでは、関数パラメーターは右から左の順にスタックにプッシュされます。 関数の戻り値はEAXレジスタに返されます(x87レジスタST0に返される浮動小数点値を除く)。 レジスターEAX、ECX、およびEDXは、この関数で使用できます。


一般に、パラメーターはスタックを逆の順序で渡され、結果の値は浮動小数点数を除いてEAXになります-x87疑似スタックになります。



作業計画を作成します。

1)メモリ内にバッファを生成します。これは変更せずに、スタック上のワードごと(4バイト)にできます。

2)呼び出す関数のアドレスを見つける

3)単語に従ってスタックバッファに置く

4)関数を呼び出す

5)結果を引き出す



行こう!





私たちは何を持っています:

1)char * sName-ここに関数の名前があります

2)int N-パラメーターの数

3)enum CParamType {cptNone = 0、cptPointer、cptInt、cptDouble}-可能なデータ型-これまでのところ、これらに限定します

4)CParamType Params []-パラメータタイプのリスト

5)void * ParamList []-実際には、パラメーターを持つ変数へのポインター

6)CParamType RetType-結果データ型

7)void * Ret-結果をスローするメモリへのポインタ

8)enum CCallConvention {cccNone = 0、cccCDecl、cccStdCall、cccFastCall}-呼び出し規約の種類

9)CCallConvention conv-呼び出し規約。 まず、cdecl関数のみを呼び出します



これは、私たちが呼び出す必要がある広告の必要十分なリストです。

C / C ++では、この操作を実装する手段がないため、アセンブラに頼る必要があります。



1.バッファーを作成する



まず、単語の数を数えます。 すべてが単純です-void *、int-4バイト-1ワード、double-8バイト-2ワード。

Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  1. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  2. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  3. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  4. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  5. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  6. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  7. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  8. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  9. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  10. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  11. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  12. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  13. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }



  14. Copy Source | Copy HTML int WordCount= 0 ; for ( int i= 0 ,i<N,i++) { switch (Params[i]) { case cptPointer: case cptInt: WordCount++; break ; case cptDouble: WordCount+= 2 ; break ; } }







カウントされます。 メモリを割り当てる:

void* Buffer = new char[4*WordCount];







バッファを埋めます:void *、int-変更せずに置き、単語をダブルスワップします。

Copy Source | Copy HTML



  1. int offset = 0 ;
  2. ダブル x;
  3. forint i = 0 、i <N、i ++)
  4. {
  5. スイッチ (Params [i])
  6. {
  7. ケース cptPointer:
  8. ケース cptInt:
  9. *( int *)(buf + offset)= *(( int *)(ParamList [i]));
  10. オフセット+ = 4 ;
  11. 休憩 ;
  12. ケース cptDouble:
  13. x = *(( double *)(((DTMain *)(v-> T))-> pData));
  14. memcpy(buf + offset + 4 、&​​x、 4 );
  15. memcpy(buf + offset、( char *)&x + 4、4 );
  16. オフセット+ = 8 ;
  17. 休憩 ;
  18. }
  19. }




コメントすることは何もないと思います。 offset-バッファによるオフセット。



2.関数のアドレスを調べる



ここではすべてが非常に簡単です。

void* addr = dlsym(NULL,sName);





最初のパラメーターはライブラリー記述子です。 現在のコンテキストで検索する場合はNULL。

dlfcn.hを接続し、リンクパラメーターに-ldlを追加することを忘れないでください。



3.単語に従ってバッファをバッファに配置します



ふう。 最も興味深いこと。

スタックを使用するには、当然アセンブラが必要です。 私はgnuコンパイラーを使用しているため、AT&T構文を使用するアセンブラーは機能しません。自分自身はあまり好きではありませんが、選択する必要はありません。

Copy Source | Copy HTML



  1. asm( "\ <br/> movl $ 0、%% eax; \ <br/> movl%2、%% ebx; \ <br/> movl%3、%% ecx; \ <br/> l1:cmpl% %ecx、%% eax; \ <br/> je l2; \ <br/> pushl(%% ebx、%% eax、4); \ <br/> addl $ 1、%% eax; \ <br/> jmp l1; "
  2. "= r" (b)
  3. "r" (addr)、 "r"バッファー )、 "g" (WordCount)
  4. "%eax"
  5. );




ループを作成します:ecx(WordCount)が0になるまで、単語をスタックに配置してecxを減らします。



4.関数を呼び出します





やる

l2: call *%1;





スタックを埋めた後。 %1は、関数(addr)へのポインターです。



5.結果を返す





2つのオプションがあります:結果全体または分数。 合意によると、デフォルトでは結果は%eaxになりますが、浮動小数点の場合はx87 all-stackになります。

1)結果全体

movl %%eax, %0;





ここで、%0は結果変数です。



2)浮動小数点オプション

理論的には、ここでST(0)から回答を削除する必要があります。 これまでのところ、私はこれを行うことができませんでした。 コメントで可能な解決策を見たいと思います。 事前に感謝します。



さて、それだけです! タスクは本当に簡単ではありませんでした。 誰かがこの投稿を必要としていることを願っています。



PSインタプリタを書くためにこれらすべてが必要です。

_________

テキストはHabraで準備されます



UPD:ソースを強調



All Articles