プログラミング言語が違うと実行バイナリも違うのか検証した話
別々のプログラミング言語で書いたHelloWorld出力プログラムの実行ファイルの中身は同じなのか違うのか気になる感あるな
— タナベ (@warito2323) 2017年12月1日
こんな話が流れてきたので検証してみる.
とりあえず実行バイナリを直接吐き出す言語として,C,C++,Go,Haskellを試してみる.チョイスの理由は単に今の環境に入っていたことと,ネイティブ実行できるバイナリを吐き出す言語をこれくらいしか知らなかったため.
実行環境はGentoo Linux(amd64),C・C++コンパイラはgcc 5.4.0-r3,Goコンパイラはgc 1.9.1,Haskellコンパイラはghc 8.0.2を用いる.
まずはコードから.
C言語
#include <stdio.h> int main() { printf("Hello, World!\n"); }
#include <cstdio> int main() { printf("Hello, World!\n"); return 0; }
Go(wikipediaから拝借)
package main import "fmt" func main() { fmt.Print("Hello, World!\n") }
main = putStrLn "Hello, World!"
コンパイルしていく.
% gcc -o hello_c hello.c % g++ -o hello_cpp hello.cpp % go build -o hello_go hello.go % ghc -o hello_hs hello.hs
コンパイルして出来たバイナリのファイルサイズをまず確認してみる.
% ls -lh total 3.0M -rwxr-xr-x 1 vicco vicco 7.8K Dec 2 22:21 hello_c* -rw-r--r-- 1 vicco vicco 63 Dec 2 01:01 hello.c -rwxr-xr-x 1 vicco vicco 7.8K Dec 2 22:21 hello_cpp* -rw-r--r-- 1 vicco vicco 73 Dec 2 00:56 hello.cpp -rwxr-xr-x 1 vicco vicco 1.8M Dec 2 22:21 hello_go* -rw-r--r-- 1 vicco vicco 74 Dec 2 22:16 hello.go -rw-r--r-- 1 vicco vicco 753 Dec 2 22:22 hello.hi -rwxr-xr-x 1 vicco vicco 1.2M Dec 2 22:22 hello_hs* -rw-r--r-- 1 vicco vicco 32 Dec 2 22:19 hello.hs -rw-r--r-- 1 vicco vicco 3.5K Dec 2 22:22 hello.o
hello_cがC,hello_cppがC++,hello_goがGo,hello_hsがHaskellの出力ファイルである.CとC++のファイルサイズが7.8KBと同じであり,対してHaskellは1.2MB,Goは1.8MBと割とでかい.
次にreadelfコマンドでELFヘッダを見てみる.ELFヘッダとは実行バイナリに何が含まれているかを記述するためにファイルの先頭に置かれている64バイトのヘッダである(詳しくはman elfを読もう).大体の部分はどのファイルも同じなので,プログラムヘッダの数とセクションヘッダの数だけ見てみよう.
% for hello in hello_c hello_cpp hello_go hello_hs; do echo $hello; readelf -h $hello|grep Number; done hello_c Number of program headers: 9 Number of section headers: 29 hello_cpp Number of program headers: 9 Number of section headers: 29 hello_go Number of program headers: 7 Number of section headers: 23 hello_hs Number of program headers: 9 Number of section headers: 31
CとC++のプログラムヘッダおよびセクションヘッダの数は一致している.一方,GoはプログラムヘッダもセクションヘッダもC(C++)よりも少ない.Haskellはセクションヘッダの数がC(C++)より多い.これはC(C++)とGoおよびHaskellではバイナリのフォーマットが異なることを意味する.
次にlddコマンドを用いて依存している共有ライブラリを見てみる.
% for hello in hello_c hello_cpp hello_go hello_hs; do echo $hello; ldd $hello; done hello_c linux-vdso.so.1 (0x00007fff2d5f2000) libc.so.6 => /lib64/libc.so.6 (0x00007f8e3b208000) /lib64/ld-linux-x86-64.so.2 (0x00007f8e3b5b9000) hello_cpp linux-vdso.so.1 (0x00007ffee2316000) libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6 (0x00007fd4f1253000) libm.so.6 => /lib64/libm.so.6 (0x00007fd4f0f43000) libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libgcc_s.so.1 (0x00007fd4f0d2c000) libc.so.6 => /lib64/libc.so.6 (0x00007fd4f097b000) /lib64/ld-linux-x86-64.so.2 (0x00007fd4f164c000) hello_go not a dynamic executable hello_hs linux-vdso.so.1 (0x00007fffe59a9000) libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f17177fb000) libm.so.6 => /lib64/libm.so.6 (0x00007f17174eb000) librt.so.1 => /lib64/librt.so.1 (0x00007f17172e3000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f17170df000) libffi.so.6 => /usr/lib64/libffi.so.6 (0x00007f1716ed6000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f1716cb6000) libc.so.6 => /lib64/libc.so.6 (0x00007f1716905000) /lib64/ld-linux-x86-64.so.2 (0x00007f1717a73000)
CとC++はファイルサイズやELFヘッダは同じだったが,C++の方が使用する共有ライブラリの数が多い.使用する共有ライブラリの情報は.dynamicセクションに格納されており,実行時に.interpセクションに格納された動的リンカ(ローダ?)がリンクしてくれる.
7shi.hateblo.jp
https://qiita.com/saikoro-steak/items/5d90ae34b61c16f6b3cfqiita.com
注目すべきはGoであり,なんと一切共有ライブラリを必要としていない.つまり実行に必要なライブラリは全て静的リンクされている.実はGoはツールチェインを自前で実装しているらしく,このためlibc相当の機能(printfやscanfのような関数)も自前で賄っている.
本の虫: goのgcコンパイラーがC実装を除去
では,コンパイラの一番の仕事だと思われる,高級言語から実際にどのようなマシン語を吐き出しているのかを見ていきたい.まずは.textセクションのサイズを見ていこう.
% for hello in hello_c hello_cpp hello_go hello_hs; do echo $hello; readelf -S $hello|grep .text -A1; done # 一部編集 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align hello_c [12] .text PROGBITS 00000000004003e0 000003e0 0000000000000171 0000000000000000 AX 0 0 16 hello_cpp [12] .text PROGBITS 00000000004004a0 000004a0 0000000000000171 0000000000000000 AX 0 0 16 hello_go [ 1] .text PROGBITS 0000000000401000 00001000 0000000000086b5b 0000000000000000 AX 0 0 16 hello_hs [12] .text PROGBITS 0000000000402cf0 00002cf0 000000000008e2d9 0000000000000000 AX 0 0 16
C,C++が0x171(=369)バイトであるのに対し,Goは0x86b5b(=551771)バイト,0x8e2d9(=582361)バイトとかなり大きくなっている.
Go,Haskellは流石にでかすぎて読めないのでCとC++を逆アセンブルしていく.CとC++で先頭のアドレス部分が全て異なっていたので,awkでざっくりと取り除いてdiffコマンドで差を見ていく.
% objdump -d hello_c|awk '$1 ~ /^[0-9a-f]+:/{$1=""; print; next}; //{print}' > c.dump % objdump -d hello_cpp|awk '$1 ~ /^[0-9a-f]+:/{$1=""; print; next}; //{print}' > cpp.dump % diff -u c.dump cpp.dump --- c.dump 2017-12-03 00:09:39.872448835 +0900 +++ cpp.dump 2017-12-03 00:09:45.972448843 +0900 @@ -1,33 +1,33 @@ -hello_c: file format elf64-x86-64 +hello_cpp: file format elf64-x86-64 Disassembly of section .init: -00000000004003a8 <_init>: +0000000000400468 <_init>: 48 83 ec 08 sub $0x8,%rsp - 48 8b 05 45 0c 20 00 mov 0x200c45(%rip),%rax # 600ff8 <_DYNAMIC+0x1d8> + 48 8b 05 7d 0b 20 00 mov 0x200b7d(%rip),%rax # 600ff0 <_DYNAMIC+0x200> 48 85 c0 test %rax,%rax - 74 02 je 4003ba <_init+0x12> + 74 02 je 40047a <_init+0x12> ff d0 callq *%rax 48 83 c4 08 add $0x8,%rsp c3 retq Disassembly of section .plt: -00000000004003c0 <puts@plt-0x10>: - ff 35 42 0c 20 00 pushq 0x200c42(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> - ff 25 44 0c 20 00 jmpq *0x200c44(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> +0000000000400480 <puts@plt-0x10>: + ff 35 82 0b 20 00 pushq 0x200b82(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> + ff 25 84 0b 20 00 jmpq *0x200b84(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 0f 1f 40 00 nopl 0x0(%rax) -00000000004003d0 <puts@plt>: - ff 25 42 0c 20 00 jmpq *0x200c42(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> +0000000000400490 <puts@plt>: + ff 25 82 0b 20 00 jmpq *0x200b82(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 68 00 00 00 00 pushq $0x0 - e9 e0 ff ff ff jmpq 4003c0 <_init+0x18> + e9 e0 ff ff ff jmpq 400480 <_init+0x18> Disassembly of section .text: -00000000004003e0 <_start>: +00000000004004a0 <_start>: 31 ed xor %ebp,%ebp 49 89 d1 mov %rdx,%r9 5e pop %rsi @@ -35,10 +35,10 @@ 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 50 push %rax 54 push %rsp - 49 c7 c0 50 05 40 00 mov $0x400550,%r8 - 48 c7 c1 f0 04 40 00 mov $0x4004f0,%rcx - 48 c7 c7 d6 04 40 00 mov $0x4004d6,%rdi - ff 15 e6 0b 20 00 callq *0x200be6(%rip) # 600ff0 <_DYNAMIC+0x1d0> + 49 c7 c0 10 06 40 00 mov $0x400610,%r8 + 48 c7 c1 b0 05 40 00 mov $0x4005b0,%rcx + 48 c7 c7 96 05 40 00 mov $0x400596,%rdi + ff 15 2e 0b 20 00 callq *0x200b2e(%rip) # 600ff8 <_DYNAMIC+0x208> f4 hlt 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) b8 37 10 60 00 mov $0x601037,%eax @@ -46,10 +46,10 @@ 48 2d 30 10 60 00 sub $0x601030,%rax 48 89 e5 mov %rsp,%rbp 48 83 f8 0e cmp $0xe,%rax - 76 1b jbe 400440 <_start+0x60> + 76 1b jbe 400500 <_start+0x60> b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 11 je 400440 <_start+0x60> + 74 11 je 400500 <_start+0x60> bf 30 10 60 00 mov $0x601030,%edi 5d pop %rbp ff e0 jmpq *%rax @@ -69,10 +69,10 @@ 48 c1 e8 3f shr $0x3f,%rax 48 01 c6 add %rax,%rsi 48 d1 fe sar %rsi - 74 15 je 400488 <_start+0xa8> + 74 15 je 400548 <_start+0xa8> b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 0b je 400488 <_start+0xa8> + 74 0b je 400548 <_start+0xa8> bf 30 10 60 00 mov $0x601030,%edi 5d pop %rbp ff e0 jmpq *%rax @@ -80,40 +80,40 @@ 5d pop %rbp c3 retq 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) - 80 3d 99 0b 20 00 00 cmpb $0x0,0x200b99(%rip) # 601030 <__TMC_END__> - 75 11 jne 4004aa <_start+0xca> + 80 3d d9 0a 20 00 00 cmpb $0x0,0x200ad9(%rip) # 601030 <__TMC_END__> + 75 11 jne 40056a <_start+0xca> 55 push %rbp 48 89 e5 mov %rsp,%rbp - e8 6e ff ff ff callq 400410 <_start+0x30> + e8 6e ff ff ff callq 4004d0 <_start+0x30> 5d pop %rbp - c6 05 86 0b 20 00 01 movb $0x1,0x200b86(%rip) # 601030 <__TMC_END__> + c6 05 c6 0a 20 00 01 movb $0x1,0x200ac6(%rip) # 601030 <__TMC_END__> c3 retq 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) - bf 18 0e 60 00 mov $0x600e18,%edi + bf e8 0d 60 00 mov $0x600de8,%edi 48 83 3f 00 cmpq $0x0,(%rdi) - 75 05 jne 4004c0 <_start+0xe0> - eb 93 jmp 400450 <_start+0x70> + 75 05 jne 400580 <_start+0xe0> + eb 93 jmp 400510 <_start+0x70> 0f 1f 00 nopl (%rax) b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 f1 je 4004bb <_start+0xdb> + 74 f1 je 40057b <_start+0xdb> 55 push %rbp 48 89 e5 mov %rsp,%rbp ff d0 callq *%rax 5d pop %rbp - e9 7a ff ff ff jmpq 400450 <_start+0x70> + e9 7a ff ff ff jmpq 400510 <_start+0x70> -00000000004004d6 <main>: +0000000000400596 <main>: 55 push %rbp 48 89 e5 mov %rsp,%rbp - bf 64 05 40 00 mov $0x400564,%edi - e8 ec fe ff ff callq 4003d0 <puts@plt> + bf 24 06 40 00 mov $0x400624,%edi + e8 ec fe ff ff callq 400490 <puts@plt> b8 00 00 00 00 mov $0x0,%eax 5d pop %rbp c3 retq 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) -00000000004004f0 <__libc_csu_init>: +00000000004005b0 <__libc_csu_init>: 41 57 push %r15 41 89 ff mov %edi,%r15d 41 56 push %r14 @@ -121,15 +121,15 @@ 41 55 push %r13 49 89 d5 mov %rdx,%r13 41 54 push %r12 - 4c 8d 25 00 09 20 00 lea 0x200900(%rip),%r12 # 600e08 <__init_array_start> + 4c 8d 25 10 08 20 00 lea 0x200810(%rip),%r12 # 600dd8 <__init_array_start> 55 push %rbp - 48 8d 2d 00 09 20 00 lea 0x200900(%rip),%rbp # 600e10 <__init_array_end> + 48 8d 2d 10 08 20 00 lea 0x200810(%rip),%rbp # 600de0 <__init_array_end> 53 push %rbx 4c 29 e5 sub %r12,%rbp 48 83 ec 08 sub $0x8,%rsp - e8 8b fe ff ff callq 4003a8 <_init> + e8 8b fe ff ff callq 400468 <_init> 48 c1 fd 03 sar $0x3,%rbp - 74 1b je 40053e <__libc_csu_init+0x4e> + 74 1b je 4005fe <__libc_csu_init+0x4e> 31 db xor %ebx,%ebx 0f 1f 00 nopl (%rax) 4c 89 ea mov %r13,%rdx @@ -138,7 +138,7 @@ 41 ff 14 dc callq *(%r12,%rbx,8) 48 83 c3 01 add $0x1,%rbx 48 39 eb cmp %rbp,%rbx - 75 ea jne 400528 <__libc_csu_init+0x38> + 75 ea jne 4005e8 <__libc_csu_init+0x38> 48 83 c4 08 add $0x8,%rsp 5b pop %rbx 5d pop %rbp @@ -149,12 +149,12 @@ c3 retq 0f 1f 00 nopl (%rax) -0000000000400550 <__libc_csu_fini>: +0000000000400610 <__libc_csu_fini>: c3 retq Disassembly of section .fini: -0000000000400554 <_fini>: +0000000000400614 <_fini>: 48 83 ec 08 sub $0x8,%rsp 48 83 c4 08 add $0x8,%rsp c3 retq
printfはコンパイラによる最適化の結果putsに置き換わっている.見比べてみると概ねjmpやcall先のアドレスが異なる程度で,後は全く同じであることが分かる.つまり,CとC++ではHello, World!程度なら同じマシン語で動くことが分かった.
以上から,同じHello, World!を出力するプログラムであっても実行バイナリは異なることが分かった,特にCやC++のような速度や最適化が売りの言語等と比較すると,GoやHaskellだとバイナリのサイズが200倍近く大きかったりする.また,実行バイナリにはマシン語だけが記述されているわけではないので,同じマシン語のプログラムであっても違う共有ライブラリに依存していたりすると実行バイナリの中身は違ってくる.今回は29個あったセクションのうち半分も比較していないので,興味がある人は他のセクションも比較してみたり,中に何が入っているのかを調べてみてほしい.バイナリの触り方を学びたい場合はBINARY HACKSがおすすめです.
Binary Hacks ―ハッカー秘伝のテクニック100選
ということで,CとC++のHello, World!は大体同じということにして終わりたいが,このままだと「printf?なんでstd::coutを使わないんだ!」って言われそうなので,ざっくりと検証してみる.以下余談なのでオチはない.
コードは以下のような感じ.
#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; }
% gcc -o hello2_cpp hello2.cpp % ls -lh hello{,2}_cpp -rwxr-xr-x 1 vicco vicco 8.4K Dec 3 00:13 hello2_cpp* -rwxr-xr-x 1 vicco vicco 7.8K Dec 2 22:21 hello_cpp*
ファイルサイズが違う.
% for hello in hello_cpp hello2_cpp; do echo $hello; readelf -h $hello|grep Number; done hello_cpp Number of program headers: 9 Number of section headers: 29 hello2_cpp Number of program headers: 9 Number of section headers: 29
ヘッダの数は同じ.
% for hello in hello_cpp hello2_cpp; do echo $hello; ldd $hello; done hello_cpp linux-vdso.so.1 (0x00007ffcc98fc000) libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6 (0x00007fa14b001000) libm.so.6 => /lib64/libm.so.6 (0x00007fa14acf1000) libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libgcc_s.so.1 (0x00007fa14aada000) libc.so.6 => /lib64/libc.so.6 (0x00007fa14a729000) /lib64/ld-linux-x86-64.so.2 (0x00007fa14b3fa000) hello2_cpp linux-vdso.so.1 (0x00007fff0d59e000) libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libstdc++.so.6 (0x00007f96e4996000) libm.so.6 => /lib64/libm.so.6 (0x00007f96e4686000) libgcc_s.so.1 => /usr/lib/gcc/x86_64-pc-linux-gnu/5.4.0/libgcc_s.so.1 (0x00007f96e446f000) libc.so.6 => /lib64/libc.so.6 (0x00007f96e40be000) /lib64/ld-linux-x86-64.so.2 (0x00007f96e4d8f000)
共有ライブラリは同じ(意外だった).
% for hello in hello_cpp hello2_cpp; do echo $hello; readelf -S $hello|grep .text -A1; done hello_cpp #一部編集 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [12] .text PROGBITS 00000000004004a0 000004a0 0000000000000171 0000000000000000 AX 0 0 16 hello2_cpp [12] .text PROGBITS 0000000000400720 00000720 00000000000001d1 0000000000000000 AX 0 0 16
.textセクションのサイズは違う.
% objdump -d hello2_cpp |awk '$1 ~ /^[0-9a-f]+:/{$1=""; print; next}; //{print}' > cpp2.dump % diff -u cpp.dump cpp2.dump --- cpp.dump 2017-12-03 00:09:45.972448843 +0900 +++ cpp2.dump 2017-12-03 00:25:56.002450091 +0900 @@ -1,33 +1,58 @@ -hello_cpp: file format elf64-x86-64 +hello2_cpp: file format elf64-x86-64 Disassembly of section .init: -0000000000400468 <_init>: +0000000000400698 <_init>: 48 83 ec 08 sub $0x8,%rsp - 48 8b 05 7d 0b 20 00 mov 0x200b7d(%rip),%rax # 600ff0 <_DYNAMIC+0x200> + 48 8b 05 4d 09 20 00 mov 0x20094d(%rip),%rax # 600ff0 <_DYNAMIC+0x200> 48 85 c0 test %rax,%rax - 74 02 je 40047a <_init+0x12> + 74 02 je 4006aa <_init+0x12> ff d0 callq *%rax 48 83 c4 08 add $0x8,%rsp c3 retq Disassembly of section .plt: -0000000000400480 <puts@plt-0x10>: - ff 35 82 0b 20 00 pushq 0x200b82(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> - ff 25 84 0b 20 00 jmpq *0x200b84(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> +00000000004006b0 <_ZNSt8ios_base4InitC1Ev@plt-0x10>: + ff 35 52 09 20 00 pushq 0x200952(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> + ff 25 54 09 20 00 jmpq *0x200954(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 0f 1f 40 00 nopl 0x0(%rax) -0000000000400490 <puts@plt>: - ff 25 82 0b 20 00 jmpq *0x200b82(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> +00000000004006c0 <_ZNSt8ios_base4InitC1Ev@plt>: + ff 25 52 09 20 00 jmpq *0x200952(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 68 00 00 00 00 pushq $0x0 - e9 e0 ff ff ff jmpq 400480 <_init+0x18> + e9 e0 ff ff ff jmpq 4006b0 <_init+0x18> + +00000000004006d0 <__cxa_atexit@plt>: + ff 25 4a 09 20 00 jmpq *0x20094a(%rip) # 601020 <_GLOBAL_OFFSET_TABLE_+0x20> + 68 01 00 00 00 pushq $0x1 + e9 d0 ff ff ff jmpq 4006b0 <_init+0x18> + +00000000004006e0 <_ZNSt8ios_base4InitD1Ev@plt>: + ff 25 42 09 20 00 jmpq *0x200942(%rip) # 601028 <_GLOBAL_OFFSET_TABLE_+0x28> + 68 02 00 00 00 pushq $0x2 + e9 c0 ff ff ff jmpq 4006b0 <_init+0x18> + +00000000004006f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>: + ff 25 3a 09 20 00 jmpq *0x20093a(%rip) # 601030 <_GLOBAL_OFFSET_TABLE_+0x30> + 68 03 00 00 00 pushq $0x3 + e9 b0 ff ff ff jmpq 4006b0 <_init+0x18> + +0000000000400700 <_ZNSolsEPFRSoS_E@plt>: + ff 25 32 09 20 00 jmpq *0x200932(%rip) # 601038 <_GLOBAL_OFFSET_TABLE_+0x38> + 68 04 00 00 00 pushq $0x4 + e9 a0 ff ff ff jmpq 4006b0 <_init+0x18> + +0000000000400710 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@plt>: + ff 25 2a 09 20 00 jmpq *0x20092a(%rip) # 601040 <_GLOBAL_OFFSET_TABLE_+0x40> + 68 05 00 00 00 pushq $0x5 + e9 90 ff ff ff jmpq 4006b0 <_init+0x18> Disassembly of section .text: -00000000004004a0 <_start>: +0000000000400720 <_start>: 31 ed xor %ebp,%ebp 49 89 d1 mov %rdx,%r9 5e pop %rsi @@ -35,22 +60,22 @@ 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 50 push %rax 54 push %rsp - 49 c7 c0 10 06 40 00 mov $0x400610,%r8 - 48 c7 c1 b0 05 40 00 mov $0x4005b0,%rcx - 48 c7 c7 96 05 40 00 mov $0x400596,%rdi - ff 15 2e 0b 20 00 callq *0x200b2e(%rip) # 600ff8 <_DYNAMIC+0x208> + 49 c7 c0 f0 08 40 00 mov $0x4008f0,%r8 + 48 c7 c1 90 08 40 00 mov $0x400890,%rcx + 48 c7 c7 16 08 40 00 mov $0x400816,%rdi + ff 15 ae 08 20 00 callq *0x2008ae(%rip) # 600ff8 <_DYNAMIC+0x208> f4 hlt 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) - b8 37 10 60 00 mov $0x601037,%eax + b8 5f 10 60 00 mov $0x60105f,%eax 55 push %rbp - 48 2d 30 10 60 00 sub $0x601030,%rax + 48 2d 58 10 60 00 sub $0x601058,%rax 48 89 e5 mov %rsp,%rbp 48 83 f8 0e cmp $0xe,%rax - 76 1b jbe 400500 <_start+0x60> + 76 1b jbe 400780 <_start+0x60> b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 11 je 400500 <_start+0x60> - bf 30 10 60 00 mov $0x601030,%edi + 74 11 je 400780 <_start+0x60> + bf 58 10 60 00 mov $0x601058,%edi 5d pop %rbp ff e0 jmpq *%rax 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) @@ -60,60 +85,92 @@ 0f 1f 40 00 nopl 0x0(%rax) 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 00 00 00 - be 30 10 60 00 mov $0x601030,%esi + be 58 10 60 00 mov $0x601058,%esi 55 push %rbp - 48 81 ee 30 10 60 00 sub $0x601030,%rsi + 48 81 ee 58 10 60 00 sub $0x601058,%rsi 48 89 e5 mov %rsp,%rbp 48 c1 fe 03 sar $0x3,%rsi 48 89 f0 mov %rsi,%rax 48 c1 e8 3f shr $0x3f,%rax 48 01 c6 add %rax,%rsi 48 d1 fe sar %rsi - 74 15 je 400548 <_start+0xa8> + 74 15 je 4007c8 <_start+0xa8> b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 0b je 400548 <_start+0xa8> - bf 30 10 60 00 mov $0x601030,%edi + 74 0b je 4007c8 <_start+0xa8> + bf 58 10 60 00 mov $0x601058,%edi 5d pop %rbp ff e0 jmpq *%rax 0f 1f 00 nopl (%rax) 5d pop %rbp c3 retq 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) - 80 3d d9 0a 20 00 00 cmpb $0x0,0x200ad9(%rip) # 601030 <__TMC_END__> - 75 11 jne 40056a <_start+0xca> + 80 3d 99 09 20 00 00 cmpb $0x0,0x200999(%rip) # 601170 <_ZSt4cout@@GLIBCXX_3.4+0x110> + 75 11 jne 4007ea <_start+0xca> 55 push %rbp 48 89 e5 mov %rsp,%rbp - e8 6e ff ff ff callq 4004d0 <_start+0x30> + e8 6e ff ff ff callq 400750 <_start+0x30> 5d pop %rbp - c6 05 c6 0a 20 00 01 movb $0x1,0x200ac6(%rip) # 601030 <__TMC_END__> + c6 05 86 09 20 00 01 movb $0x1,0x200986(%rip) # 601170 <_ZSt4cout@@GLIBCXX_3.4+0x110> c3 retq 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) bf e8 0d 60 00 mov $0x600de8,%edi 48 83 3f 00 cmpq $0x0,(%rdi) - 75 05 jne 400580 <_start+0xe0> - eb 93 jmp 400510 <_start+0x70> + 75 05 jne 400800 <_start+0xe0> + eb 93 jmp 400790 <_start+0x70> 0f 1f 00 nopl (%rax) b8 00 00 00 00 mov $0x0,%eax 48 85 c0 test %rax,%rax - 74 f1 je 40057b <_start+0xdb> + 74 f1 je 4007fb <_start+0xdb> 55 push %rbp 48 89 e5 mov %rsp,%rbp ff d0 callq *%rax 5d pop %rbp - e9 7a ff ff ff jmpq 400510 <_start+0x70> + e9 7a ff ff ff jmpq 400790 <_start+0x70> -0000000000400596 <main>: +0000000000400816 <main>: 55 push %rbp 48 89 e5 mov %rsp,%rbp - bf 24 06 40 00 mov $0x400624,%edi - e8 ec fe ff ff callq 400490 <puts@plt> + be 04 09 40 00 mov $0x400904,%esi + bf 60 10 60 00 mov $0x601060,%edi + e8 c7 fe ff ff callq 4006f0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt> + be 10 07 40 00 mov $0x400710,%esi + 48 89 c7 mov %rax,%rdi + e8 ca fe ff ff callq 400700 <_ZNSolsEPFRSoS_E@plt> b8 00 00 00 00 mov $0x0,%eax 5d pop %rbp c3 retq - 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) -00000000004005b0 <__libc_csu_init>: +000000000040083d <_Z41__static_initialization_and_destruction_0ii>: + 55 push %rbp + 48 89 e5 mov %rsp,%rbp + 48 83 ec 10 sub $0x10,%rsp + 89 7d fc mov %edi,-0x4(%rbp) + 89 75 f8 mov %esi,-0x8(%rbp) + 83 7d fc 01 cmpl $0x1,-0x4(%rbp) + 75 27 jne 400878 <_Z41__static_initialization_and_destruction_0ii+0x3b> + 81 7d f8 ff ff 00 00 cmpl $0xffff,-0x8(%rbp) + 75 1e jne 400878 <_Z41__static_initialization_and_destruction_0ii+0x3b> + bf 71 11 60 00 mov $0x601171,%edi + e8 5c fe ff ff callq 4006c0 <_ZNSt8ios_base4InitC1Ev@plt> + ba 50 10 60 00 mov $0x601050,%edx + be 71 11 60 00 mov $0x601171,%esi + bf e0 06 40 00 mov $0x4006e0,%edi + e8 58 fe ff ff callq 4006d0 <__cxa_atexit@plt> + 90 nop + c9 leaveq + c3 retq + +000000000040087b <_GLOBAL__sub_I_main>: + 55 push %rbp + 48 89 e5 mov %rsp,%rbp + be ff ff 00 00 mov $0xffff,%esi + bf 01 00 00 00 mov $0x1,%edi + e8 af ff ff ff callq 40083d <_Z41__static_initialization_and_destruction_0ii> + 5d pop %rbp + c3 retq + +0000000000400890 <__libc_csu_init>: 41 57 push %r15 41 89 ff mov %edi,%r15d 41 56 push %r14 @@ -121,15 +178,15 @@ 41 55 push %r13 49 89 d5 mov %rdx,%r13 41 54 push %r12 - 4c 8d 25 10 08 20 00 lea 0x200810(%rip),%r12 # 600dd8 <__init_array_start> + 4c 8d 25 28 05 20 00 lea 0x200528(%rip),%r12 # 600dd0 <__init_array_start> 55 push %rbp - 48 8d 2d 10 08 20 00 lea 0x200810(%rip),%rbp # 600de0 <__init_array_end> + 48 8d 2d 30 05 20 00 lea 0x200530(%rip),%rbp # 600de0 <__init_array_end> 53 push %rbx 4c 29 e5 sub %r12,%rbp 48 83 ec 08 sub $0x8,%rsp - e8 8b fe ff ff callq 400468 <_init> + e8 db fd ff ff callq 400698 <_init> 48 c1 fd 03 sar $0x3,%rbp - 74 1b je 4005fe <__libc_csu_init+0x4e> + 74 1b je 4008de <__libc_csu_init+0x4e> 31 db xor %ebx,%ebx 0f 1f 00 nopl (%rax) 4c 89 ea mov %r13,%rdx @@ -138,7 +195,7 @@ 41 ff 14 dc callq *(%r12,%rbx,8) 48 83 c3 01 add $0x1,%rbx 48 39 eb cmp %rbp,%rbx - 75 ea jne 4005e8 <__libc_csu_init+0x38> + 75 ea jne 4008c8 <__libc_csu_init+0x38> 48 83 c4 08 add $0x8,%rsp 5b pop %rbx 5d pop %rbp @@ -149,12 +206,12 @@ c3 retq 0f 1f 00 nopl (%rax) -0000000000400610 <__libc_csu_fini>: +00000000004008f0 <__libc_csu_fini>: c3 retq Disassembly of section .fini: -0000000000400614 <_fini>: +00000000004008f4 <_fini>: 48 83 ec 08 sub $0x8,%rsp 48 83 c4 08 add $0x8,%rsp c3 retq
diffを見ていくと,printfを使うhello_cppとstd::coutを使うhello2_cppではシンボル名が大きく異なることに気がつく.これはC++の名前マングリングによるものであり,例えばmain関数で呼んでいる_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_をc++filtコマンドでデマングルしてみると以下のような結果が得られる.
% c++filt _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_ std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
シンボル名自体に名前空間やクラス,引数の情報が含まれていることが分かる.この辺は全く知らないのでマサカリが怖い.
シンボル名の違いの他,いくつかのコードが追加されている.大きな違いは.pltセクションにいくつかの関数が追加されていること,mainで呼んでいる関数がputsではないこと,__static_initialization_and_destruction_0と_GLOBAL__sub_I_mainという関数が追加されていることだろうか.__static_initialization_and_destruction_0はstd::ios_base::Init::Init()といった初期化処理を行っているが,これを呼び出している_GLOBAL__sub_I_mainはどこから呼び出しているのかが分からなかった.何か分かったらいつかまとめよう.
stackoverflow.com