Cプログラムはどのようにして開始されますか?
オペレーティングシステムは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の機能を説明していると思います。
最終的にはオペレーティングシステムです。通常、実際のエントリポイントとメイン関数の間には何らかの媒体があり、これは コンパイラ リンカ。
いくつかの詳細(Windowsに関連):IMAGE_OPTIONAL_HEADER
という名前のPEファイルにヘッダーがあり、フィールドAddressOfEntryPoint
があります。これは、実行されるファイルの最初のコードバイトのアドレスです。
オペレーティングシステムはmainを呼び出します。再配置可能実行可能ファイルには、mainの場所を指すアドレスがあります(詳細については、Unix ABIを参照してください)。
しかし、誰がオペレーティングシステムを呼び出しますか?
中央処理装置は、「リセット」信号(これも電源投入時にアサートされます)で、指定されたアドレス(たとえば、0xffff)でいくつかのROM)を調べてその命令を探し始めます。
通常、BIOSへの何らかのジャンプ命令が出て、メモリチップが構成され、基本的なハードドライブドライバーが読み込まれるなどします。次に、ハードドライブのブートセクターが読み取られ、nextブートローダーが開始され、NTFSパーティションの読み取り方法やカーネルファイル自体の読み取り方法などの基本情報を含むファイルがロードされます。カーネル環境がセットアップされ、カーネルがロードされ、次に-、次に!-カーネルは実行のためにジャンプされます。
すべてのハードワークが完了した後、カーネルはソフトウェアのロードに進むことができます。
オペレーティングシステムは、Cランタイム(CRT)に含まれ、実行可能ファイルにリンクされた関数を呼び出します。これを「CRTメイン」と呼びます。
CRT mainはいくつかのことを行いますが、そのうちの最も重要な2つは、少なくともC++では、グローバルC++クラスの配列を実行してそれらのコンストラクターを呼び出すことと、main()関数を呼び出してその戻り値をシェルに渡すことです。
メモリが機能する場合、Visual C++ CRTメインはさらにいくつかのことを行います。メモリアロケータを構成します。DebugCRTを使用してメモリリークや不正なアクセスを見つける場合に重要です。また、不正なメモリアクセスやその他のクラッシュをキャッチして表示する 構造化例外 ハンドラ内でmainを呼び出します。
おそらくあなたの質問に最適な情報は、下記のリンク http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html で見つけることができます。これまでに出会った中で最も優れたものです。
すでに投稿されている回答に加えて、main
を自分で呼び出すこともできます。一般に、これは難読化されたコード用に予約された悪い考えです。