C プログラムと dynamic linker の entry point を追う
前提: x86_64 linux 環境
C のプログラムなら、普通は main()
からはじまる。
C Standard で定まっているはず。
でも、これは実際の実行ファイル (ELF) の entry point ではない。
ELF の entry point は、ELF header の e_entry
を見ればわかる。
普通、実行ファイルなら _start
という symbol になっている。
ふつう、_start
は crt1.o
とかみたいな名前の object file から来ている (ld
がデフォルトでリンクしてくれる)。
crt は c runtime らしいよ
P.S.
shared object だと _start
はあったりなかったり? あまりよくわかってないが...
_start が無い場合は .text の先頭だったり、0x0 だったり?
_start
を entry point に設定しているのは誰? というと、たぶん ld
になる。
ld --verbose
を実行すると、デフォルトの linker script が出てくる。
ENTRY(_start)
こんな行があれば、entry point が _start
という symbol になっているといって良さそう。
ld
の -e
オプションとか、カスタムの linker script で上書きできるらしい。
でも、ほんとに _start
から実行がはじまるの? というと、dynamic linker が関わる場合はそうでもない気がしてくる。
というのは、Program header に PT_INTERP
がある場合、Program interpreter が実行したい実行ファイルより先に起動して、ライブラリロードやら (lazy bind ではない) 動的リンクやらを済ませてしまってから指定した実行ファイルの _start
にジャンプしてくるはずだから。
つまり、dynamic linker の entry point こそ (PT_INTERP
を含むプログラムの実行という意味では) 真に entry point と言えるような気もしてくる。
gdb で starti
を実行すると、プログラム実行の最初の命令で止まる。
これは、ld-linux-x86-64.so.2
の _start
になっていることが多いと思う。
手元で試したときは、こんな感じ
0x7ffff7fe4540 <_start> mov %rsp,%rdi
0x7ffff7fe4543 <_start+3> call 0x7ffff7fe51d0 <_dl_start>
2 つめの命令ですぐ _dl_start
に飛ぶ。
なんだか、crt1.o
にある普通の startup routine とは様子が違う感じ
ld.so
の entry point はどこから来てるのか。
_dl_start
は glibc の elf/rtld.c
にいるみたいだったが、定義自体は得に変わったところもなく普通の関数に見える。
Makefile
をよく読んでみても、カスタムの linker script を使っている様子もなくどこから entry point が来るのかよくわからない。
Compile option に -nostdlib
, -nostartfiles
やらついていて、どうやら普通の starup routine を使っていないらしいことはわかる。
さらに探すと、sysdeps/
以下の dl-machine.h
に _start
の定義があるのがわかった。
RTLD_START
というマクロに asm
で startup routine が埋め込まれており、これは結局 rtld.c
の中で展開されている。
gdb で見たやつとも一致してるし、startup はこの部分で間違い無さそう
/* Initial entry point code for the dynamic linker.
The C function `_dl_start' is the real entry point;
its return value is the user program's entry point. */
#define RTLD_START asm ("\n\
.text\n\
.align 16\n\
.globl _start\n\
.globl _dl_start_user\n\
_start:\n\
movq %rsp, %rdi\n\
call _dl_start\n\
_dl_start_user:\n\
# Save the user entry point address in %r12.\n\
(略)
とりあえず今日調べてわかったことを忘れないうちにまとめておいた。
TODO: 気が向いたらもうちょっとまとめて記事にするかも?