web-dev-qa-db-ja.com

Cでmain()なしでプログラムをコンパイルして実行する

Cmain()関数なしで次のプログラムをコンパイルして実行しようとしています。次のコマンドを使用してプログラムをコンパイルしました。

gcc -nostartfiles nomain.c

そして、コンパイラは警告を出します

/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340

はい、問題ありません。次に、実行可能ファイル(a.out)を実行しました。両方のprintfステートメントが正常に印刷され、セグメンテーションエラーが表示されます。

だから、私の質問は、なぜ印刷ステートメントを正常に実行した後にセグメンテーション違反ですか?

私のコード:

#include <stdio.h>

void nomain()
{
        printf("Hello World...\n");
        printf("Successfully run without main...\n");
}

出力:

Hello World...
Successfully run without main...
Segmentation fault (core dumped)

注:

ここに、 -nostartfiles gccフラグは、コンパイラーがリンク時に標準の起動ファイルを使用できないようにします

78
msc

プログラムで生成された Assembly を見てみましょう。

_.LC0:
        .string "Hello World..."
.LC1:
        .string "Successfully run without main..."
nomain:
        Push    rbp
        mov     rbp, rsp
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        mov     edi, OFFSET FLAT:.LC1
        call    puts
        nop
        pop     rbp
        ret
_

retステートメントに注意してください。プログラムのエントリポイントはnomainであると判断され、それで問題ありません。しかし、関数が戻ると、呼び出しスタックのアドレスにジャンプしようとします...それは設定されていません。それは不正アクセスであり、セグメンテーション違反が続きます。

簡単な解決策は、プログラムの最後でexit()を呼び出すことです(C11を想定して、関数を__Noreturn_とマークすることもできます)。

_#include <stdio.h>
#include <stdlib.h>

_Noreturn void nomain(void)
{
    printf("Hello World...\n");
    printf("Successfully run without main...\n");
    exit(0);
}
_

実際、関数は通常のmain関数とほとんど同じように動作します。これは、mainから戻った後、exit関数がmainの戻り値で呼び出されるためです。 。

129
StoryTeller

Cでは、関数/サブルーチンが呼び出されると、スタックは次のように(順番に)入力されます。

  1. 引数、
  2. 差出人住所、
  3. ローカル変数-> スタックの最上部

main()が開始点であるため、ELFは、最初に来る命令が最初にプッシュされるようにプログラムを構造化します。この場合、printfsはそうです。

これで、プログラムはreturn-address OR __end__そして、実際には、そのスタックにあるものは何でも想定しています(__end__)locationは返信先アドレスですが、残念ながらそうではないため、クラッシュします。

21
Milind Deore