web-dev-qa-db-ja.com

main()の前にC / C ++プログラムがクラッシュする方法はありますか?

Main()の前にプログラムがクラッシュする方法はありますか?

63
Thi

Gccを使用すると、関数に コンストラクタ属性 をタグ付けできます(これにより、mainの前に関数が実行されます)。次の関数では、premainmainの前に呼び出されます。

#include <stdio.h>

void premain() __attribute__ ((constructor));

void premain()
{
    fputs("premain\n", stdout);
}

int main()
{
    fputs("main\n", stdout);
    return 0;
}

したがって、premainにクラッシュするバグがある場合、mainの前にクラッシュします。

36

はい、少なくともWindowsでは。プログラムがDLLを利用する場合、main()が開始する前にDLLをロードできます。これらのDLLのDllMain関数は、main()の前に実行されます。エラーが発生した場合、プロセス全体が停止またはクラッシュする可能性があります。

33
Anders Abel

C++プログラムがある場合、mainに入る前に、関数とコンストラクターを介して変数とオブジェクトを初期化できます。これらのいずれかのバグにより、プログラムがクラッシュする可能性があります。

20
epatel

簡単な答えははいです。

具体的には、2つの原因を区別できます。それらをimplementation-dependentおよびimplementation-independentと呼びます。

環境にまったく依存しない1つのケースは、C++の静的オブジェクトのケースで、ここで説明しました。次のコードはmain()の前に終了します。

_#include <iostream>

class Useless {
public:
    Useless() { throw "You can't construct me!"; }

};

static Useless object;

int main() {
    std::cout << "This will never be printed" << std::endl;

    return 0;
}
_

より興味深いのはプラットフォーム依存の原因です。いくつかはここで言及されました。ここで何度か言及されたのは、動的にリンクされたライブラリ(WindowsのDLL、LinuxのSOなど)の使用でした-OSのローダーがmain()の前にそれらをロードすると、アプリケーションが実行する可能性がありますmain()の前に死ぬ。

この原因のより一般的なバージョンは、your entry point(main())を呼び出す前にyour binaryのエントリポイントが行うすべてのことについて話します。通常、バイナリをビルドすると、オペレーティングシステムのローダーがバイナリの実行を開始したときに呼び出されるかなり深刻なコードブロックがあり、実行が完了するとmain()が呼び出されます。このコードが行う一般的なことの1つは、C/C++標準ライブラリの初期化です。このコードは、さまざまな理由で失敗する可能性があります(1つに割り当てようとするあらゆる種類のシステムリソースの不足)。

Windowsでmain()の前にバイナリがコードを実行する興味深い方法の1つは、TLSコールバックを使用することです(Googleがそれらについて詳しく説明します)。この手法は通常、マルウェアで基本的なデバッグ防止のトリックとして発見されます(このトリックは、当時ollydbgをだますために使用されていましたが、まだそうであるかどうかはわかりません)。

ポイントは、あなたの質問は実際には「バイナリをロードするとユーザーコードがmain()のコードの前に実行される方法があるのですか」と同等であり、答えはhell、yeah !

18
conio

確かにC++で。コンストラクタを持つ静的オブジェクトはメインの前に呼び出されます-それらは死ぬ可能性があります

cについてわからない

ここにサンプルがあります

class X
{
public:
X()
{
  char *x = 0;
  *x = 1;
}
};

X x;
int main()
{
return 0;
}

これはメインの前にクラッシュします

18
pm100

メインの前に読み込まれる共有オブジェクト(DLL)に依存するプログラムは、メインの前に失敗する可能性があります。

Linuxでは、ダイナミックリンカーライブラリ(ld-*。so)のコードが実行されて、メインのかなり前にライブラリの依存関係が提供されます。必要なライブラリが見つからない場合、それらにアクセスできない、通常のファイルではない、またはプログラムをリンクしたダイナミックリンカーが必要とするシンボルを持たない場合は、それはあなたのプログラムをリンクし、これは失敗を引き起こす可能性があります。

さらに、各ライブラリは、リンクされたときにいくつかのコードを実行します。これは主に、ライブラリがより多くのライブラリをリンクする必要がある場合や、いくつかのコンストラクタを実行する必要がある場合があるためです(Cプログラムの場合でも、ライブラリには、C++など、コンストラクタを使用するものがあります)。さらに、標準のCプログラムでは、stdio FILEのstdin、stdout、およびstderrがすでに作成されています。多くのシステムでは、これらも閉じることができます。これは、それらもfree()されていることを意味します。これは、それら(およびそれらのバッファー)がmalloc()され、失敗する可能性があることを意味します。また、それらのFILE構造が表すファイル記述子に対して他の処理を行った可能性があり、失敗する可能性があることも示唆しています。

OSがプログラムに渡された環境変数やコマンドライン引数、あるいはその両方の設定をめちゃくちゃにしていた場合、他に起こり得る可能性があります。 mainの前のコードは、mainを呼び出す前にこのデータで何かしなければならなかった可能性があります。

メインの前にたくさんのことが起こります。それらはどれも致命的な方法で失敗する可能性があります。

10
nategoose

わかりませんが、次のようなグローバル変数がある場合:

static SomeClass object;

int main(){
   return 0;
}

'SomeClass'コンストラクターは、メインが実行される前にプログラムをクラッシュさせる可能性があります。

6

多くの可能性があります。

まず、mainが実行される前に、実際に何が行われているのかを理解する必要があります。

  • 動的ライブラリーのロード
  • グローバルの初期化
  • 一部のコンパイラ、一部の関数は明示的に実行できます

これにより、いくつかの方法でクラッシュが発生する可能性があります。

  • 通常の未定義の動作(nullポインターの逆参照、メモリへのアクセスは禁止されています...)
  • 例外がスローされました> catchがないため、terminateが呼び出され、プログラムが終了します

もちろん、これは本当に厄介で、デバッグが難しい可能性があります。そのため、mainの前のコードの実行をできるだけ控え、可能な場合は遅延初期化、またはmain内の明示的な初期化を優先する必要があります。

もちろん、DLLが失敗して修正できない場合は、苦痛の世界にいます。

5
Matthieu M.

並べ替え: http://blog.ksplice.com/2010/03/libc-free-world/

次のように、標準ライブラリなしでコンパイルする場合:gcc -nostdlib -o hello hello.c

main()の実行方法がわからず、クラッシュします。

4
MK.

これは「メインの前」の意味に依存しますが、「メインのコードが実際に実行される前」を意味する場合は、1つの例が考えられます。大きな配列をメインのローカル変数として宣言すると、この配列のサイズが使用可能なスタックスペースを超えている場合、stack overflow mainへのエントリ時、コードの最初の行が実行される前。

3
Paul R

C++プログラムのグローバルオブジェクトと静的オブジェクトは、main()の最初のステートメントが実行される前にコンストラクターが呼び出されるため、コンストラクターの1つにバグがあるとクラッシュする可能性があります。

ただし、これはCプログラムでは発生しません。

3
ronys
class Crash
{
public:
  Crash( int* p )
  { *p = 0; }
};

static Crash static_crash( 0 );

void main()
{
}
2
smocoder

やや工夫された例は次のとおりです。

int a = 1;
int b = 0;
int c = a / b;

int main()
{
    return 0;
}

このようなことをすることはほとんどありませんが、多くのマクロマジックを実行している場合、それは完全に可能です。

2
Zoli

私は同じ問題に直面していました。見つかった根本的な原因は..メインプロセスで初期化されたローカル変数(巨大な配列)が多すぎるため、ローカル変数のサイズが1.5 mbを超えていました。
スタックポインタが非常に大きく、OSがこのジャンプを無効として検出し、悪意のあるプログラムをクラッシュさせるため、これは大きなジャンプをもたらします。

これをデバッグするには。
1。 GDBを起動する
2。メインにブレークポイントを追加する
3。メインを分解
4。サブ$ 0xGGGGGGG、%espを確認します
このGGGGGG値が高すぎる場合、私と同じ問題が表示されます。

メインのすべてのローカル変数の合計サイズを確認してください。

1
knightofhorizon

あなたはどのプラットフォーム/ libcを言っていません。組み込みの世界では、多くの場合、main()の前に実行される多くのものがあり、主にプラットフォームのセットアップに関係しており、これはうまくいかない可能性があります。 (実際に、通常のOSでファンキーなリンカースクリプトを使用している場合、すべての賭けはオフになっていますが、それはかなりまれです。)

1
crazyscot

一部のプラットフォーム抽象化ライブラリーはオーバーライドします(個人的にはQtやACEなどのC++ライブラリーしか知りませんが、これを行うCライブラリーも同様です) "main"。これにより、int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );といくつかのライブラリスタッフを設定し、コマンドライン引数を通常の_int argc, char* argv[]_に変換してから、通常のint main(int argc, char* argv[])を呼び出します

もちろん、そのようなライブラリは、これを正しく実装しなかった場合にクラッシュする可能性があります(コマンドライン引数の形式が正しくない可能性があります)。

そして、これについて知らない人にとって、これはmainの前にクラッシュのように見えるかもしれません

1
smerlin

確かに、オペレーティングシステムまたはランタイムコードにバグがある場合。 C++はこの動作で特に悪名高いですが、Cでも発生する可能性があります。

1
Carl Norum

スタックのmainの前にプログラムをクラッシュさせるのに最適な例オーバーフロー

int main() {
    char volatile stackoverflow[1000000000] = {0};
    return 0;
}
0
imix