0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HelloWorld本 第5章

Last updated at Posted at 2024-02-09

プログラム実行の流れ

  • _start()
  • __libc_start_main()
  • exit(main())

_start()

glibc/sysdeps/i386/start.Sに定義される。

# _start()はエントリーポイントであり、テキストセグメントの先頭に配置される。
# edxはatexit()に登録される関数ポインタを持ち、espは引数や環境変数を指すが、他のレジスタは規定されない。
_start:
    # 最外フレームを明示する為、フレームポインタをクリアする。
    xorl %ebp, %ebp

    # esiにargc、ecxにargvを設定する。
    # envpは__libc_start_main()で設定される。
    popl %esi
    movl %esp, %ecx

    # SSE命令は16バイトのアライメントを要求する。
    # 28バイト使用する為、4バイトのパディングを行う。
    andl $0xfffffff0, %esp
    pushl %eax

    # スタックとatexit()に登録される関数ポインタを渡す。
    pushl %esp
    pushl %edx

    # .initと.finiを渡す。
    pushl $__libc_csu_fini
    pushl $__libc_csu_init

    # argcとargvを渡す。
    pushl %ecx
    pushl %esi

    # main()のアドレスを渡す。
    pushl $main

    # __libc_start_main()を呼ぶ。
    call __libc_start_main

    # exit()で終了しない場合はクラッシュさせる。
    hlt

__libc_start_main()

glibc/csu/libc-start.cに定義される。

# LIBC_START_MAINは__libc_start_mainに置き換わる。
#define LIBC_START_MAIN __libc_start_main

static int LIBC_START_MAIN(int (*main)(int, char **, char **),
                           int argc, char **argv,
                           int (*init)(int, char **, char **), void (*fini)(void),
                           void (*rtld_fini)(void), void *stack_end) {
    # 環境変数を設定する。
    __environ = &argv[argc + 1];

    # 関数を登録し、初期化を行う。
    if(rtld_fini) {
        __cxa_atexit(rtld_fini, NULL, NULL);
    }
    if(fini) {
        __cxa_atexit(fini, NULL, NULL);
    }
    if(init) {
        init(argc, argv, __environ);
    }

    # exit(main())を呼び出す。
    exit(main(argc, argv, __environ));
}

exit()

_exit()はhlt命令を含む為、アーキテクチャ依存のアセンブラを調べる。
glibc/sysdeps/unix/sysv/linux/i386/_exit.Sに定義される。

_exit:
    # ステータスをシステムコールに渡す。
    movl 4(%esp), %ebx

# exit_groupを実行する。
# ENTER_KERNELはシステムコールを実行するマクロである。
#ifdef __NR_exit_group
    movl $__NR_exit_group, %eax
    ENTER_KERNEL
#endif

    # exit_groupが存在しない場合、exitを実行する。
    movl $__NR_exit, %eax
    int $0x80

    # プログラムが終了しない場合はクラッシュさせる。
    hlt

exitは呼び出したスレッド、exit_groupは全スレッドを終了させるシステムコールである。
_exit()はexitやexit_groupを実行し、プログラムを終了させるシステムコールラッパーである。
exit()はatexit()に登録された関数を実行した後、_exit()を呼び出す。

APIとABI

カーネルはシステムコールのABIであるexitやexit_groupを提供し、glibcはPOSIXのAPIであるexit()や_exit()を実装する。
FreeBSDにはexit_groupは存在しないが、カーネルの差はシステムコールラッパーで埋められる。

カーネル側の処理

glibcを見てきたが、カーネル側の処理も確認する。
UNIXライクなシステムでは、fork()とexec()でプログラムを開始する。
exec()はexecveを呼び出し、以下の処理を行う。

  • 実行ファイルを仮想メモリに配置する
  • 引数やデータ領域、環境変数を準備する
  • エントリーポイントにジャンプする

コマンド

  • manのカテゴリ1はコマンド、カテゴリ2はシステムコールラッパー、カテゴリ3はライブラリ関数である
  • straceを使用するとexit_groupの呼び出しが確認できる
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?