libc無しにHello World
[やりたいこと]
・なるべく小さな実行ファイル(バイナリ)を作る
・そして、そのバイナリでアセンブラの勉強をする
[環境]
・CentOS7 x64
[Cソースコード]
以下のWikiの「標準Cライブラリなし」ソースをそのまま使わせてもらいました。
・エントリポイントとなる関数は _start()という決まりがある
・その他の関数 _exit(), write()はインラインアセンブラで書かれている
現状、インラインアセンブラで何が書いているのか、理解していないです。
[コンパイル]
コンパイルは書かれている通りのコマンドでできました。
-Osを付けてアセンブラの最適化、-Wl,-sを付けてstripして、それぞれ容量が小さくなることも確認しました。
[逆アセンブル]
[root@cent7 ]# objdump -d -Mintel nostdlib nostdlib: ファイル形式 elf64-x86-64 セクション .text の逆アセンブル: 000000000040010c: 40010c: 55 push rbp 40010d: 48 89 e5 mov rbp,rsp 400110: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi 400114: 48 89 75 f0 mov QWORD PTR [rbp-0x10],rsi 400118: 48 89 55 e8 mov QWORD PTR [rbp-0x18],rdx 40011c: 48 8b 4d f8 mov rcx,QWORD PTR [rbp-0x8] 400120: 4c 8b 45 f0 mov r8,QWORD PTR [rbp-0x10] 400124: 4c 8b 4d e8 mov r9,QWORD PTR [rbp-0x18] 400128: 48 c7 c0 01 00 00 00 mov rax,0x1 40012f: 48 89 cf mov rdi,rcx 400132: 4c 89 c6 mov rsi,r8 400135: 4c 89 ca mov rdx,r9 400138: 0f 05 syscall 40013a: 5d pop rbp 40013b: c3 ret 000000000040013c <_exit>: 40013c: 55 push rbp 40013d: 48 89 e5 mov rbp,rsp 400140: 48 89 7d f8 mov QWORD PTR [rbp-0x8],rdi 400144: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8] 400148: 48 c7 c0 3c 00 00 00 mov rax,0x3c 40014f: 48 89 d7 mov rdi,rdx 400152: 0f 05 syscall 400154: 5d pop rbp 400155: c3 ret 0000000000400156 <_start>: 400156: 55 push rbp 400157: 48 89 e5 mov rbp,rsp 40015a: ba 0e 00 00 00 mov edx,0xe 40015f: be 7a 01 40 00 mov esi,0x40017a 400164: bf 01 00 00 00 mov edi,0x1 400169: e8 9e ff ff ff call 40010c 40016e: bf 00 00 00 00 mov edi,0x0 400173: e8 c4 ff ff ff call 40013c <_exit> 400178: 5d pop rbp 400179: c3 ret
[ダンプ]
[root@cent7 ]# od -Ax -tx1z nostdlib 000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 >.ELF............< 000010 02 00 3e 00 01 00 00 00 56 01 40 00 00 00 00 00 >..>.....V.@.....< 000020 40 00 00 00 00 00 00 00 f8 03 00 00 00 00 00 00 >@...............< 000030 00 00 00 00 40 00 38 00 03 00 40 00 09 00 06 00 >....@.8...@.....< 000040 01 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 >................< 000050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 >..@.......@.....< 000060 08 02 00 00 00 00 00 00 08 02 00 00 00 00 00 00 >................< 000070 00 00 20 00 00 00 00 00 04 00 00 00 04 00 00 00 >.. .............< 000080 e8 00 00 00 00 00 00 00 e8 00 40 00 00 00 00 00 >..........@.....< 000090 e8 00 40 00 00 00 00 00 24 00 00 00 00 00 00 00 >..@.....$.......< 0000a0 24 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 >$...............< 0000b0 51 e5 74 64 06 00 00 00 00 00 00 00 00 00 00 00 >Q.td............< 0000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< * 0000e0 10 00 00 00 00 00 00 00 04 00 00 00 14 00 00 00 >................< 0000f0 03 00 00 00 47 4e 55 00 7a 05 6c f3 61 6a 18 92 >....GNU.z.l.aj..< 000100 6c c7 09 08 8b f5 53 1f b5 fd 2b db 55 48 89 e5 >l.....S...+.UH..< 000110 48 89 7d f8 48 89 75 f0 48 89 55 e8 48 8b 4d f8 >H.}.H.u.H.U.H.M.< 000120 4c 8b 45 f0 4c 8b 4d e8 48 c7 c0 01 00 00 00 48 >L.E.L.M.H......H< 000130 89 cf 4c 89 c6 4c 89 ca 0f 05 5d c3 55 48 89 e5 >..L..L....].UH..< 000140 48 89 7d f8 48 8b 55 f8 48 c7 c0 3c 00 00 00 48 >H.}.H.U.H..<...H< 000150 89 d7 0f 05 5d c3 55 48 89 e5 ba 0e 00 00 00 be >....].UH........< 000160 7a 01 40 00 bf 01 00 00 00 e8 9e ff ff ff bf 00 >z.@.............< 000170 00 00 00 e8 c4 ff ff ff 5d c3 48 65 6c 6c 6f 2c >........].Hello,< 000180 20 77 6f 72 6c 64 21 0a 00 00 00 00 00 00 00 00 > world!.........< 000190 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01 >.........zR..x..< 0001a0 1b 0c 07 08 90 01 00 00 1c 00 00 00 1c 00 00 00 >................< 0001b0 5c ff ff ff 30 00 00 00 00 41 0e 10 86 02 43 0d >\...0....A....C.< 0001c0 06 6b 0c 07 08 00 00 00 1c 00 00 00 3c 00 00 00 >.k..........<...< 0001d0 6c ff ff ff 1a 00 00 00 00 41 0e 10 86 02 43 0d >l........A....C.< 0001e0 06 55 0c 07 08 00 00 00 1c 00 00 00 5c 00 00 00 >.U..........\...< 0001f0 66 ff ff ff 24 00 00 00 00 41 0e 10 86 02 43 0d >f...$....A....C.< 000200 06 5f 0c 07 08 00 00 00 47 43 43 3a 20 28 47 4e >._......GCC: (GN< 000210 55 29 20 34 2e 38 2e 35 20 32 30 31 35 30 36 32 >U) 4.8.5 2015062< 000220 33 20 28 52 65 64 20 48 61 74 20 34 2e 38 2e 35 >3 (Red Hat 4.8.5< 000230 2d 31 31 29 00 00 2e 73 79 6d 74 61 62 00 2e 73 >-11)...symtab..s< 000240 74 72 74 61 62 00 2e 73 68 73 74 72 74 61 62 00 >trtab..shstrtab.< 000250 2e 6e 6f 74 65 2e 67 6e 75 2e 62 75 69 6c 64 2d >.note.gnu.build-< 000260 69 64 00 2e 74 65 78 74 00 2e 72 6f 64 61 74 61 >id..text..rodata< 000270 00 2e 65 68 5f 66 72 61 6d 65 00 2e 63 6f 6d 6d >..eh_frame..comm< 000280 65 6e 74 00 00 00 00 00 00 00 00 00 00 00 00 00 >ent.............< 000290 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0002a0 00 00 00 00 03 00 01 00 e8 00 40 00 00 00 00 00 >..........@.....< 0002b0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 02 00 >................< 0002c0 0c 01 40 00 00 00 00 00 00 00 00 00 00 00 00 00 >..@.............< 0002d0 00 00 00 00 03 00 03 00 7a 01 40 00 00 00 00 00 >........z.@.....< 0002e0 00 00 00 00 00 00 00 00 00 00 00 00 03 00 04 00 >................< 0002f0 90 01 40 00 00 00 00 00 00 00 00 00 00 00 00 00 >..@.............< 000300 00 00 00 00 03 00 05 00 00 00 00 00 00 00 00 00 >................< 000310 00 00 00 00 00 00 00 00 01 00 00 00 04 00 f1 ff >................< 000320 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 000330 0c 00 00 00 02 00 02 00 0c 01 40 00 00 00 00 00 >..........@.....< 000340 30 00 00 00 00 00 00 00 12 00 00 00 02 00 02 00 >0...............< 000350 3c 01 40 00 00 00 00 00 1a 00 00 00 00 00 00 00 ><.@.............< 000360 18 00 00 00 12 00 02 00 56 01 40 00 00 00 00 00 >........V.@.....< 000370 24 00 00 00 00 00 00 00 1f 00 00 00 10 00 04 00 >$...............< 000380 00 10 60 00 00 00 00 00 00 00 00 00 00 00 00 00 >..`.............< 000390 2b 00 00 00 10 00 04 00 00 10 60 00 00 00 00 00 >+.........`.....< 0003a0 00 00 00 00 00 00 00 00 32 00 00 00 10 00 04 00 >........2.......< 0003b0 00 10 60 00 00 00 00 00 00 00 00 00 00 00 00 00 >..`.............< 0003c0 00 6e 6f 73 74 64 6c 69 62 2e 63 00 77 72 69 74 >.nostdlib.c.writ< 0003d0 65 00 5f 65 78 69 74 00 5f 73 74 61 72 74 00 5f >e._exit._start._< 0003e0 5f 62 73 73 5f 73 74 61 72 74 00 5f 65 64 61 74 >_bss_start._edat< 0003f0 61 00 5f 65 6e 64 00 00 00 00 00 00 00 00 00 00 >a._end..........< 000400 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< * 000430 00 00 00 00 00 00 00 00 1b 00 00 00 07 00 00 00 >................< 000440 02 00 00 00 00 00 00 00 e8 00 40 00 00 00 00 00 >..........@.....< 000450 e8 00 00 00 00 00 00 00 24 00 00 00 00 00 00 00 >........$.......< 000460 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 >................< 000470 00 00 00 00 00 00 00 00 2e 00 00 00 01 00 00 00 >................< 000480 06 00 00 00 00 00 00 00 0c 01 40 00 00 00 00 00 >..........@.....< 000490 0c 01 00 00 00 00 00 00 6e 00 00 00 00 00 00 00 >........n.......< 0004a0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 >................< 0004b0 00 00 00 00 00 00 00 00 34 00 00 00 01 00 00 00 >........4.......< 0004c0 02 00 00 00 00 00 00 00 7a 01 40 00 00 00 00 00 >........z.@.....< 0004d0 7a 01 00 00 00 00 00 00 0f 00 00 00 00 00 00 00 >z...............< 0004e0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 >................< 0004f0 00 00 00 00 00 00 00 00 3c 00 00 00 01 00 00 00 >........<.......< 000500 02 00 00 00 00 00 00 00 90 01 40 00 00 00 00 00 >..........@.....< 000510 90 01 00 00 00 00 00 00 78 00 00 00 00 00 00 00 >........x.......< 000520 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 >................< 000530 00 00 00 00 00 00 00 00 46 00 00 00 01 00 00 00 >........F.......< 000540 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >0...............< 000550 08 02 00 00 00 00 00 00 2d 00 00 00 00 00 00 00 >........-.......< 000560 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 >................< 000570 01 00 00 00 00 00 00 00 11 00 00 00 03 00 00 00 >................< 000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 000590 35 02 00 00 00 00 00 00 4f 00 00 00 00 00 00 00 >5.......O.......< 0005a0 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 >................< 0005b0 00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 >................< 0005c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 0005d0 88 02 00 00 00 00 00 00 38 01 00 00 00 00 00 00 >........8.......< 0005e0 08 00 00 00 09 00 00 00 08 00 00 00 00 00 00 00 >................< 0005f0 18 00 00 00 00 00 00 00 09 00 00 00 03 00 00 00 >................< 000600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >................< 000610 c0 03 00 00 00 00 00 00 37 00 00 00 00 00 00 00 >........7.......< 000620 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 >................< 000630 00 00 00 00 00 00 00 00 >........< 000638
[コードセクション]
[root@cent7 ]# readelf -S nostdlib 9 個のセクションヘッダ、始点オフセット 0x3f8: セクションヘッダ: [番] 名前 タイプ アドレス オフセット サイズ EntSize フラグ Link 情報 整列 [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .note.gnu.build-i NOTE 00000000004000e8 000000e8 0000000000000024 0000000000000000 A 0 0 4 [ 2] .text PROGBITS 000000000040010c 0000010c 000000000000006e 0000000000000000 AX 0 0 1 [ 3] .rodata PROGBITS 000000000040017a 0000017a 000000000000000f 0000000000000000 A 0 0 1 [ 4] .eh_frame PROGBITS 0000000000400190 00000190 0000000000000078 0000000000000000 A 0 0 8 [ 5] .comment PROGBITS 0000000000000000 00000208 000000000000002d 0000000000000001 MS 0 0 1 [ 6] .shstrtab STRTAB 0000000000000000 00000235 000000000000004f 0000000000000000 0 0 1 [ 7] .symtab SYMTAB 0000000000000000 00000288 0000000000000138 0000000000000018 8 9 8 [ 8] .strtab STRTAB 0000000000000000 000003c0 0000000000000037 0000000000000000 0 0 1 フラグのキー: W (write), A (alloc), X (実行), M (merge), S (文字列), l (large) I (情報), L (リンク順), G (グループ), T (TLS), E (排他), x (不明) O (追加の OS 処理が必要) o (OS 固有), p (プロセッサ固有)
[ToDo]
・インラインアセンブラを理解する
volatileの影響をアセンブリで比較
volatileはコンパイラによる最適化を抑止する修飾子のことだった気がする。
volatileの有無により、アセンブリがどう違うのかを比較してみる。
(どうでもいいけど、アセンブラとアセンブリの違いがわからない。。。)
試してみた結果、volatile有りだと演算用のレジスタ(?)にコピーしてから、加算や比較処理を行うようになっている模様。細かいことはアセンブラが読めないのでわかりません。
[shin@cent6-5 volatile]$ cat loop.c int main() { int i; for(i=0; i<1000000000; i++){} } [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ cat loop_vol.c int main() { volatile int i; for(i=0; i<1000000000; i++){} } [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ diff loop.s loop_vol.s 1c1 < .file "loop.c" --- > .file "loop_vol.c" 16c16,18 < addl $1, -4(%rbp) --- > movl -4(%rbp), %eax > addl $1, %eax > movl %eax, -4(%rbp) 18c20,21 < cmpl $999999999, -4(%rbp) --- > movl -4(%rbp), %eax > cmpl $999999999, %eax [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ cat loop.s .file "loop.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, -4(%rbp) ★変数i(rbp)を0で初期化 jmp .L2 ★L2にジャンプ .L3: addl $1, -4(%rbp) ★変数i(rbp)に1を加算 .L2: cmpl $999999999, -4(%rbp) ★ループの条件判定 jle .L3 ★判定結果に基づきL3にジャンプ leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)" .section .note.GNU-stack,"",@progbits [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ [shin@cent6-5 volatile]$ cat loop_vol.s .file "loop_vol.c" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $0, -4(%rbp) jmp .L2 .L3: movl -4(%rbp), %eax ★別のレジスタにコピー addl $1, %eax movl %eax, -4(%rbp) ★元のレジスタに書き戻し .L2: movl -4(%rbp), %eax ★別のレジスタにコピー cmpl $999999999, %eax jle .L3 leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)" .section .note.GNU-stack,"",@progbits [shin@cent6-5 volatile]$
参考にさせて頂いたサイト
gccはプリプロセス、コンパイル、アセンブル、リンク
gccはコンパイルするツールという認識しかなかったけど、プリプロセス、コンパイル、アセンブル、リンクという4つの処理をまとめて行ってくれていたらしい。
以下のコマンドで1つずつ処理を進められるので、中間ファイルを確認できる。
=> #includeや#defineなどの文を展開する
=> アセンブリ言語のソースを機械語のオブジェクトに変換する
リンク:gcc
=> オブジェクトをリンクして実行形式にする
-E, -S, -cはそれぞれ何の略かわからないけど、エスケープ(ESc)の順番だと思えば覚えられるかもしれない。。。
[shin@cent6-5 helloworld]$ gcc -E helloworld.c > helloworld.i [shin@cent6-5 helloworld]$ gcc -S helloworld.i [shin@cent6-5 helloworld]$ ll 合計 28 -rw-rw-r--. 1 shin shin 61 11月 8 01:01 2014 helloworld.c -rw-rw-r--. 1 shin shin 16736 11月 16 20:47 2014 helloworld.i -rw-rw-r--. 1 shin shin 467 11月 16 20:57 2014 helloworld.s [shin@cent6-5 helloworld]$ cat helloworld.s .file "helloworld.c" .section .rodata .LC0: .string "hello world!" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl $.LC0, %eax movq %rax, %rdi movl $0, %eax call printf leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)" .section .note.GNU-stack,"",@progbits [shin@cent6-5 helloworld]$ gcc -c helloworld.s [shin@cent6-5 helloworld]$ ll 合計 32 -rw-rw-r--. 1 shin shin 61 11月 8 01:01 2014 helloworld.c -rw-rw-r--. 1 shin shin 16736 11月 16 20:47 2014 helloworld.i -rw-rw-r--. 1 shin shin 1512 11月 16 20:58 2014 helloworld.o -rw-rw-r--. 1 shin shin 467 11月 16 20:57 2014 helloworld.s [shin@cent6-5 helloworld]$ gcc -o helloworld helloworld.o [shin@cent6-5 helloworld]$ ll 合計 40 -rwxrwxr-x. 1 shin shin 6432 11月 16 20:59 2014 helloworld -rw-rw-r--. 1 shin shin 61 11月 8 01:01 2014 helloworld.c -rw-rw-r--. 1 shin shin 16736 11月 16 20:47 2014 helloworld.i -rw-rw-r--. 1 shin shin 1512 11月 16 20:58 2014 helloworld.o -rw-rw-r--. 1 shin shin 467 11月 16 20:57 2014 helloworld.s [shin@cent6-5 helloworld]$ ./helloworld hello world!
fileコマンド
今日もファイルを調べるコマンドについて。
fileコマンドでファイルの種類がわかる。
ASCIIかUTF-8かバイナリかなど。。。
バイナリに対しては、ELF形式かとか動的リンクされてるとかstripされてるかとか、まだまだ調べ甲斐がありそう。
[shin@cent6-5 helloworld]$ file helloworld.c helloworld.c: ASCII C program text [shin@cent6-5 helloworld]$ file helloworld_jp.c helloworld_jp.c: UTF-8 Unicode C program text [shin@cent6-5 helloworld]$ file /dev/cdrom /dev/cdrom: symbolic link to `sr0' [shin@cent6-5 helloworld]$ file /dev/sr0 /dev/sr0: block special [shin@cent6-5 helloworld]$ file /mnt/ /mnt/: directory [shin@cent6-5 helloworld]$ file /proc/meminfo /proc/meminfo: empty [shin@cent6-5 helloworld]$ file a.out a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
odコマンド
Linux上ではテキストファイルがどのように扱われるのか気になったので調べてみる。
odコマンドとやらでファイルがバイナリ表示できるようだ。
-xで16進表示。
-cでアスキー表示。
[shin@cent6-5 helloworld]$ od helloworld.c -x 0000000 6923 636e 756c 6564 3c20 7473 6964 2e6f 0000020 3e68 0a0a 6f76 6469 6d20 6961 286e 7b29 0000040 200a 7020 6972 746e 2866 6822 6c65 6f6c 0000060 7720 726f 646c 2221 3b29 7d0a 000a 0000075 [shin@cent6-5 helloworld]$ od helloworld.c -c 0000000 # i n c l u d e < s t d i o . 0000020 h > \n \n v o i d m a i n ( ) { 0000040 \n p r i n t f ( " h e l l o 0000060 w o r l d ! " ) ; \n } \n 0000075