web-dev-qa-db-ja.com

Cでは、main()メソッドはどのように最初に呼び出されますか?

Cプログラムはどのようにして開始されますか?

51
user418627

オペレーティングシステムはmain()関数を呼び出します。実際には、通常、__init_のような奇妙な名前の付いた別の名前を呼び出します。 Cコンパイラは、このオペレーティングシステムで定義されたエントリポイントを提供し、main()を呼び出すすべてのアプリケーションに標準ライブラリをリンクします。

編集:明らかに、それは一部の人々にとっては詳細で十分ではありませんでした。

Executable and Linkable Format(ELF) 多くのUnix OSが使用するエントリポイントアドレスを定義します。これは、OSがexec()呼び出しを完了した後にプログラムが実行を開始する場所です。 Linuxシステムでは、これは_initです。

Objdump -dから:

_Disassembly of section .init:

08049f08 <_init>:
 8049f08:       55                      Push   %ebp
 8049f09:       89 e5                   mov    %esp,%ebp
 8049f0b:       83 ec 08                sub    $0x8,%esp
 8049f0e:       e8 a1 05 00 00          call   804a4b4 <call_gmon_start>
 8049f13:       e8 f8 05 00 00          call   804a510 <frame_dummy>
 8049f18:       e8 d3 50 00 00          call   804eff0 <__do_global_ctors_aux>
 8049f1d:       c9                      leave  
 8049f1e:       c3                      ret    
_

Readelf -dから:

_ 0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libpthread.so.0]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8049f08
 0x0000000d (FINI)                       0x804f018
 0x00000004 (HASH)                       0x8048168
 0x00000005 (STRTAB)                     0x8048d8c
 0x00000006 (SYMTAB)                     0x804867c
 0x0000000a (STRSZ)                      3313 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x8059114
 0x00000002 (PLTRELSZ)                   688 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8049c58
 0x00000011 (REL)                        0x8049be0
 0x00000012 (RELSZ)                      120 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x8049b60
 0x6fffffff (VERNEEDNUM)                 3
 0x6ffffff0 (VERSYM)                     0x8049a7e
 0x00000000 (NULL)                       0x0
_

INITが_initのアドレスに等しいことがわかります。

Frame_dummyおよび__do_global_ctors_auxのコードは、crtbegin.oおよびcrtend.o(およびこれらの名前の変形)という名前の一連のファイルにあります。これらはGCCの一部です。このコードは、stdin、stdout、グローバル変数、静的変数の設定など、Cプログラムに必要なさまざまなことを行います。

次の記事では、Linuxでの動作について詳しく説明しています(投票数を減らして以下の回答から抜粋)。 http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html

他の誰かの答えがすでにWindowsの機能を説明していると思います。

46
Zan Lynx

最終的にはオペレーティングシステムです。通常、実際のエントリポイントとメイン関数の間には何らかの媒体があり、これは コンパイラ リンカ。

いくつかの詳細(Windowsに関連):IMAGE_OPTIONAL_HEADERという名前のPEファイルにヘッダーがあり、フィールドAddressOfEntryPointがあります。これは、実行されるファイルの最初のコードバイトのアドレスです。

24
Andrey
9
user387302

オペレーティングシステムはmainを呼び出します。再配置可能実行可能ファイルには、mainの場所を指すアドレスがあります(詳細については、Unix ABIを参照してください)。

しかし、誰がオペレーティングシステムを呼び出しますか?

中央処理装置は、「リセット」信号(これも電源投入時にアサートされます)で、指定されたアドレス(たとえば、0xffff)でいくつかのROM)を調べてその命令を探し始めます。

通常、BIOSへの何らかのジャンプ命令が出て、メモリチップが構成され、基本的なハードドライブドライバーが読み込まれるなどします。次に、ハードドライブのブートセクターが読み取られ、nextブートローダーが開始され、NTFSパーティションの読み取り方法やカーネルファイル自体の読み取り方法などの基本情報を含むファイルがロードされます。カーネル環境がセットアップされ、カーネルがロードされ、次に-、次に!-カーネルは実行のためにジャンプされます。

すべてのハードワークが完了した後、カーネルはソフトウェアのロードに進むことができます。

6
Paul Nathan

オペレーティングシステムは、Cランタイム(CRT)に含まれ、実行可能ファイルにリンクされた関数を呼び出します。これを「CRTメイン」と呼びます。

CRT mainはいくつかのことを行いますが、そのうちの最も重要な2つは、少なくともC++では、グローバルC++クラスの配列を実行してそれらのコンストラクターを呼び出すことと、main()関数を呼び出してその戻り値をシェルに渡すことです。

メモリが機能する場合、Visual C++ CRTメインはさらにいくつかのことを行います。メモリアロケータを構成します。DebugCRTを使用してメモリリークや不正なアクセスを見つける場合に重要です。また、不正なメモリアクセスやその他のクラッシュをキャッチして表示する 構造化例外 ハンドラ内でmainを呼び出します。

5
Drew Hoskins

おそらくあなたの質問に最適な情報は、下記のリンク http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html で見つけることができます。これまでに出会った中で最も優れたものです。

4

すでに投稿されている回答に加えて、mainを自分で呼び出すこともできます。一般に、これは難読化されたコード用に予約された悪い考えです。

4
Brian