5

プログラムを実行するときは、パラメータを渡すことができます。

$ myProgram par1 par2 par3

argvCでは、を見てこれらのパラメータにアクセスできます。

int main (int argc, char *argv[]) 
{
     char* aParameter = argv[1];  // Not sure if this is 100% right but you get the idea...
}

これはアセンブリ/x86マシンコードでどのように変換されますか?与えられた変数にどのようにアクセスしますか?システムはこれらの変数をどのように提供しますか?

アセンブリは非常に新しいので、レジスタと絶対アドレスにしかアクセスできないようになっています。パラメータにアクセスする方法に戸惑っています。システムはパラメータを特殊レジスタにプリロードしますか?

4

3 に答える 3

9

関数呼び出し

パラメータは通常、が指すメモリの一部であるスタックに渡されますespespオペレーティングシステムは、スタック用にメモリを予約し、プログラムに制御を渡す前に適切にセットアップする責任があります。

通常の関数呼び出しは次のようになります。

main:
  push 456
  push 123
  call MyFunction
  add esp, 8
  ret

MyFunction:
   ; [esp+0] will hold the return address
   ; [esp+4] will hold the first parameter (123)
   ; [esp+8] will hold the second parameter (456)
   ;
   ; To return from here, we usually execute a 'ret' instruction,
   ; which is actually equivalent to:
   ;
   ; add esp, 4
   ; jmp [esp-4]

   ret

呼び出し元の関数と呼び出されている関数の間には、レジスターの保持をどのように約束するかに関して、さまざまな責任があります。これらのルールは、呼び出し規約と呼ばれます。

上記の例では、cdecl呼び出し規約を使用しています。これは、パラメーターが逆の順序でスタックにプッシュされ、呼び出し関数が、espそれらのパラメーターがスタックにプッシュされる前のポイントに復元する役割を果たします。それが何をするかadd esp, 8です。

主な機能

通常、main関数はアセンブリで記述し、オブジェクトファイルにアセンブルします。次に、このオブジェクトファイルをリンカに渡して実行可能ファイルを生成します。

リンカは、制御が関数に渡される前にスタックを適切に設定するスタートアップコードを生成する役割を果たしますmain。これにより、関数は2つの引数(argc / argv)で呼び出されたかのように動作できます。つまり、main関数は実際のエントリポイントではありませんが、argc / argv引数を設定した後、スタートアップコードはそこにジャンプします。

スタートアップコード

では、この「スタートアップコード」はどのように見えるのでしょうか。リンカは私たちのためにそれを生成しますが、ものがどのように機能するかを知ることは常に興味深いことです。

これはプラットフォーム固有ですが、Linuxでの典型的なケースについて説明します。この記事は、日付が付けられていますが、i386プログラムが起動したときのLinuxのスタックレイアウトについて説明しています。スタックは次のようになります。

esp+00h: argc
esp+04h: argv[0]
esp+08h: argv[1]
esp+1Ch: argv[2]
...

したがって、スタートアップコードは、スタックからargc / argv値を取得し、次のmain(...)2つのパラメーターを使用して呼び出すことができます。

; This is very incomplete startup code, but it illustrates the point

mov eax, [esp]        ; eax = argc
lea edx, [esp+0x04]   ; edx = argv

; push argv, and argc onto the stack (note the reverse order)
push edx
push eax
call main
;
; When main returns, use its return value (eax)
; to set an exit status
;
...
于 2011-12-06T00:16:44.783 に答える
4

あなたのプログラムがするのと同じように; 手動で行う必要があります。

関数への引数は、関数が呼び出される前にさまざまなレジスタ/メモリセグメントに格納されます。アセンブリで関数を呼び出すときは、呼び出す前にスタックを手動で設定する必要があります。呼び出し規約は、これらの変数がどこに行くか、それらがどのように順序付けられるか、そしてそれらがどのようにアクセスされるかを決定します。

たとえば、argcargvが作成され、スタックにプッシュされます。彼らが指すデータもすでに作成されているはずです。関数が呼び出されると、呼び出し規約に従って、引数1..nがメモリのあるセクションに配置されていることがわかります。

これは、関数を呼び出す前にスタックがどのようにセットアップされるかに関するいくつかの例とともに、呼び出し規約の簡単な要約です。

ちなみに、呼び出される前にある程度の作業を行う必要がmainあり、これはあなたには隠されています。これは良いことです。新しいプロジェクトを開始するたびに大量のブートストラップコードを記述したくはありません。

于 2011-12-05T23:26:16.930 に答える
4

Cランタイムは、ここでいくつかの作業を行っています。OSからプログラム引数をフェッチし、必要に応じて、main関数を呼び出す前にそれらを解析します。asemmblerでは、コマンド引数をフェッチして自分で解析する必要があります。プログラム引数を取得する方法は、OSによって異なります。

于 2011-12-05T23:36:41.107 に答える