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:ソースを強調

Source: https://habr.com/ru/post/J78886/


All Articles