情弱ログ

参考にならないので当てにしないでください

PIE(position-independent executable)なオブジェクトの中身を見てみる

巷ではSpectre/Meltdownが話題になっている.

有効な対策として(K)ASLRが挙げられており,これはプロセス実行時のメモリ空間の配置をランダマイズすることで,例えば攻撃者が配置したコードのアドレスへのJMP等を防ぐことができる.
詳しくはシェルコードでググろう(解説できるほど詳しくない).

共有ライブラリは原則的にPIC(position-independent code)としてビルドされており,これらはASLRの恩恵に与ることができる.

しかし,通常のアプリケーションは絶対アドレスを用いて記述されているため,ASLRによるランダマイズが適用できない.
そこで,セキュリティ向上のために相対アドレスを用いたPIE(position-independent executable)と呼ばれる形式が通常のアプリケーションにおいても使用されている.

昔はセキュリティ上重要なファイルだけがPIEでビルドされていたらしいが,最近ではGentoo Linuxのprofile 17.0からPIEでのビルドがデフォルトオプションになったりしている.(これ全パッケージのリビルドが必要なので,まだ更新できてないです.同時にpython3.4がstableに降ってきたりして酷いことになりました…)
https://www.gentoo.org/support/news-items/2017-11-30-new-17-profiles.html

しかし,PIEでビルドした場合はパフォーマンスの低下が懸念されている.論文ではベンチマークであるSPEC CPU2006を用いて評価実験を行い,幾何平均で9.4%のパフォーマンス低下という結果を得たと述べている.
だが,実際にPIEを用いることで何がボトルネックになっているのか分からないので,今回はPIEなオブジェクトの中身を見てみる.
中身が見たくて見てるので,パフォーマンス低下の考察なら論文でしてると思うよ.

検証には前回と同じファイルを用いる.また,実行環境はGNU/Linux Fedora 25を用い,コンパイラgcc 6.4.1を用いた.

#include <cstdio>

class Base {
public:
    Base() { printf("Base::Base() has called\n"); }
    virtual void hoge() { printf("Base::hoge() has called\n"); }
    virtual void fuga() { printf("Base::fuga() has called\n"); }
};

class Derived : public Base {
public:
    Derived() { printf("Derived::Derived() has called\n"); }
    void fuga() { printf("Derived::fuga() has called\n"); }
};

void func(Base &x)
{
    x.hoge();
    x.fuga();
}

int main()
{
    Derived obj;
    func(obj);

    return 0;
}

PIEとしてビルドする場合,-fPIEもしくは-fpieオプションをつけてコンパイルし,リンク時に-pieオプションをつける.

$ g++ -c -fPIE vtable.cpp
$ g++ -o vtable_pie -pie vtable.o
$ g++ -o vtable_pie -pie -fPIE vtable.cpp # 一発でやる場合
$ ./vtable_pie
Base::Base() has called
Derived::Derived() has called
Base::hoge() has called
Derived::fuga() has called

比較用に普通にビルドしたファイルも用意しておく.

$ g++ -o vtable_normal vtable.cpp
$ ./vtable_normal
Base::Base() has called
Derived::Derived() has called
Base::hoge() has called
Derived::fuga() has called

ELFヘッダを見てみる.まずは普通にビルドした方から.

$ readelf -h vtable_normal
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x4005b0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          7288 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         30
  Section header string table index: 27

次にPIEな方.

$ readelf -h vtable_pie
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x7f0
  Start of program headers:          64 (bytes into file)
  Start of section headers:          11504 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         32
  Section header string table index: 29

タイプが「EXEC (Executable file)」から「DYN (Shared object file)」になっており,またEntry point addressが小さくなっている.あとセクションヘッダの数が異なっており,そのせいかストリングテーブルのインデックスも異なっている.

セクションヘッダを見てみる.普通の方.

readelf -S vtable_normal
There are 30 section headers, starting at offset 0x1c78:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       0000000000000028  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002c0  000002c0
       00000000000000d8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400398  00000398
       0000000000000108  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           00000000004004a0  000004a0
       0000000000000012  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000004004b8  000004b8
       0000000000000040  0000000000000000   A       6     2     8
  [ 9] .rela.dyn         RELA             00000000004004f8  000004f8
       0000000000000060  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000400558  00000558
       0000000000000018  0000000000000018  AI       5    23     8
  [11] .init             PROGBITS         0000000000400570  00000570
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400590  00000590
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .text             PROGBITS         00000000004005b0  000005b0
       0000000000000272  0000000000000000  AX       0     0     16
  [14] .fini             PROGBITS         0000000000400824  00000824
       0000000000000009  0000000000000000  AX       0     0     4
  [15] .rodata           PROGBITS         0000000000400830  00000830
       0000000000000116  0000000000000000   A       0     0     8
  [16] .eh_frame_hdr     PROGBITS         0000000000400948  00000948
       0000000000000064  0000000000000000   A       0     0     4
  [17] .eh_frame         PROGBITS         00000000004009b0  000009b0
       00000000000001b4  0000000000000000   A       0     0     8
  [18] .init_array       INIT_ARRAY       0000000000600dd8  00000dd8
       0000000000000008  0000000000000000  WA       0     0     8
  [19] .fini_array       FINI_ARRAY       0000000000600de0  00000de0
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .jcr              PROGBITS         0000000000600de8  00000de8
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .dynamic          DYNAMIC          0000000000600df0  00000df0
       0000000000000200  0000000000000010  WA       6     0     8
  [22] .got              PROGBITS         0000000000600ff0  00000ff0
       0000000000000010  0000000000000008  WA       0     0     8
  [23] .got.plt          PROGBITS         0000000000601000  00001000
       0000000000000020  0000000000000008  WA       0     0     8
  [24] .data             PROGBITS         0000000000601020  00001020
       0000000000000004  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           0000000000601028  00001024
       00000000000000b8  0000000000000000  WA       0     0     8
  [26] .comment          PROGBITS         0000000000000000  00001024
       0000000000000058  0000000000000001  MS       0     0     1
  [27] .shstrtab         STRTAB           0000000000000000  00001b6e
       0000000000000108  0000000000000000           0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00001080
       00000000000007b0  0000000000000018          29    46     8
  [29] .strtab           STRTAB           0000000000000000  00001830
       000000000000033e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

PIEな方.

$ readelf -S vtable_pie
There are 32 section headers, starting at offset 0x2cf0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298
       0000000000000030  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002c8  000002c8
       0000000000000150  0000000000000018   A       6     2     8
  [ 6] .dynstr           STRTAB           0000000000000418  00000418
       000000000000012f  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           0000000000000548  00000548
       000000000000001c  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000000568  00000568
       0000000000000040  0000000000000000   A       6     2     8
  [ 9] .rela.dyn         RELA             00000000000005a8  000005a8
       00000000000001e0  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             0000000000000788  00000788
       0000000000000018  0000000000000018  AI       5    25     8
  [11] .init             PROGBITS         00000000000007a0  000007a0
       0000000000000017  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         00000000000007c0  000007c0
       0000000000000020  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         00000000000007e0  000007e0
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         00000000000007f0  000007f0
       00000000000002c2  0000000000000000  AX       0     0     16
  [15] .fini             PROGBITS         0000000000000ab4  00000ab4
       0000000000000009  0000000000000000  AX       0     0     4
  [16] .rodata           PROGBITS         0000000000000ac0  00000ac0
       0000000000000097  0000000000000000   A       0     0     8
  [17] .eh_frame_hdr     PROGBITS         0000000000000b58  00000b58
       0000000000000064  0000000000000000   A       0     0     4
  [18] .eh_frame         PROGBITS         0000000000000bc0  00000bc0
       00000000000001b4  0000000000000000   A       0     0     8
  [19] .init_array       INIT_ARRAY       0000000000201d38  00001d38
       0000000000000008  0000000000000000  WA       0     0     8
  [20] .fini_array       FINI_ARRAY       0000000000201d40  00001d40
       0000000000000008  0000000000000000  WA       0     0     8
  [21] .jcr              PROGBITS         0000000000201d48  00001d48
       0000000000000008  0000000000000000  WA       0     0     8
  [22] .data.rel.ro      PROGBITS         0000000000201d50  00001d50
       0000000000000070  0000000000000000  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000201dc0  00001dc0
       0000000000000210  0000000000000010  WA       6     0     8
  [24] .got              PROGBITS         0000000000201fd0  00001fd0
       0000000000000030  0000000000000008  WA       0     0     8
  [25] .got.plt          PROGBITS         0000000000202000  00002000
       0000000000000020  0000000000000008  WA       0     0     8
  [26] .data             PROGBITS         0000000000202020  00002020
       0000000000000004  0000000000000000  WA       0     0     1
  [27] .bss              NOBITS           0000000000202024  00002024
       0000000000000004  0000000000000000  WA       0     0     1
  [28] .comment          PROGBITS         0000000000000000  00002024
       0000000000000058  0000000000000001  MS       0     0     1
  [29] .shstrtab         STRTAB           0000000000000000  00002bd2
       0000000000000119  0000000000000000           0     0     1
  [30] .symtab           SYMTAB           0000000000000000  00002080
       00000000000007f8  0000000000000018          31    48     8
  [31] .strtab           STRTAB           0000000000000000  00002878
       000000000000035a  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

.plt.gotと.data.rel.roというセクションが増えているようだ.ググったらRELRO (RELocation Read-Only)という別?の技術が出てきたが何も分からんので宿題にしたい.

アセンブルしてみる.

 objdump -d vtable_pie |c++filt

vtable_pie:     file format elf64-x86-64


Disassembly of section .init:

00000000000007a0 <_init>:
 7a0:	48 83 ec 08          	sub    $0x8,%rsp
 7a4:	48 8b 05 25 18 20 00 	mov    0x201825(%rip),%rax        # 201fd0 <_DYNAMIC+0x210>
 7ab:	48 85 c0             	test   %rax,%rax
 7ae:	74 02                	je     7b2 <_init+0x12>
 7b0:	ff d0                	callq  *%rax
 7b2:	48 83 c4 08          	add    $0x8,%rsp
 7b6:	c3                   	retq

Disassembly of section .plt:

00000000000007c0 <puts@plt-0x10>:
 7c0:	ff 35 42 18 20 00    	pushq  0x201842(%rip)        # 202008 <_GLOBAL_OFFSET_TABLE_+0x8>
 7c6:	ff 25 44 18 20 00    	jmpq   *0x201844(%rip)        # 202010 <_GLOBAL_OFFSET_TABLE_+0x10>
 7cc:	0f 1f 40 00          	nopl   0x0(%rax)

00000000000007d0 <puts@plt>:
 7d0:	ff 25 42 18 20 00    	jmpq   *0x201842(%rip)        # 202018 <_GLOBAL_OFFSET_TABLE_+0x18>
 7d6:	68 00 00 00 00       	pushq  $0x0
 7db:	e9 e0 ff ff ff       	jmpq   7c0 <_init+0x20>

Disassembly of section .plt.got:

00000000000007e0 <.plt.got>:
 7e0:	ff 25 12 18 20 00    	jmpq   *0x201812(%rip)        # 201ff8 <_DYNAMIC+0x238>
 7e6:	66 90                	xchg   %ax,%ax

Disassembly of section .text:

00000000000007f0 <_start>:
 7f0:	31 ed                	xor    %ebp,%ebp
 7f2:	49 89 d1             	mov    %rdx,%r9
 7f5:	5e                   	pop    %rsi
 7f6:	48 89 e2             	mov    %rsp,%rdx
 7f9:	48 83 e4 f0          	and    $0xfffffffffffffff0,%rsp
 7fd:	50                   	push   %rax
 7fe:	54                   	push   %rsp
 7ff:	4c 8d 05 aa 02 00 00 	lea    0x2aa(%rip),%r8        # ab0 <__libc_csu_fini>
 806:	48 8d 0d 33 02 00 00 	lea    0x233(%rip),%rcx        # a40 <__libc_csu_init>
 80d:	48 8d 3d 45 01 00 00 	lea    0x145(%rip),%rdi        # 959 <main>
 814:	ff 15 c6 17 20 00    	callq  *0x2017c6(%rip)        # 201fe0 <_DYNAMIC+0x220>
 81a:	f4                   	hlt
 81b:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)

0000000000000820 <deregister_tm_clones>:
 820:	48 8d 3d 01 18 20 00 	lea    0x201801(%rip),%rdi        # 202028 <__TMC_END__>
 827:	48 8d 05 01 18 20 00 	lea    0x201801(%rip),%rax        # 20202f <__TMC_END__+0x7>
 82e:	55                   	push   %rbp
 82f:	48 29 f8             	sub    %rdi,%rax
 832:	48 89 e5             	mov    %rsp,%rbp
 835:	48 83 f8 0e          	cmp    $0xe,%rax
 839:	76 15                	jbe    850 <deregister_tm_clones+0x30>
 83b:	48 8b 05 a6 17 20 00 	mov    0x2017a6(%rip),%rax        # 201fe8 <_DYNAMIC+0x228>
 842:	48 85 c0             	test   %rax,%rax
 845:	74 09                	je     850 <deregister_tm_clones+0x30>
 847:	5d                   	pop    %rbp
 848:	ff e0                	jmpq   *%rax
 84a:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
 850:	5d                   	pop    %rbp
 851:	c3                   	retq
 852:	0f 1f 40 00          	nopl   0x0(%rax)
 856:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 85d:	00 00 00

0000000000000860 <register_tm_clones>:
 860:	48 8d 3d c1 17 20 00 	lea    0x2017c1(%rip),%rdi        # 202028 <__TMC_END__>
 867:	48 8d 35 ba 17 20 00 	lea    0x2017ba(%rip),%rsi        # 202028 <__TMC_END__>
 86e:	55                   	push   %rbp
 86f:	48 29 fe             	sub    %rdi,%rsi
 872:	48 89 e5             	mov    %rsp,%rbp
 875:	48 c1 fe 03          	sar    $0x3,%rsi
 879:	48 89 f0             	mov    %rsi,%rax
 87c:	48 c1 e8 3f          	shr    $0x3f,%rax
 880:	48 01 c6             	add    %rax,%rsi
 883:	48 d1 fe             	sar    %rsi
 886:	74 18                	je     8a0 <register_tm_clones+0x40>
 888:	48 8b 05 61 17 20 00 	mov    0x201761(%rip),%rax        # 201ff0 <_DYNAMIC+0x230>
 88f:	48 85 c0             	test   %rax,%rax
 892:	74 0c                	je     8a0 <register_tm_clones+0x40>
 894:	5d                   	pop    %rbp
 895:	ff e0                	jmpq   *%rax
 897:	66 0f 1f 84 00 00 00 	nopw   0x0(%rax,%rax,1)
 89e:	00 00
 8a0:	5d                   	pop    %rbp
 8a1:	c3                   	retq
 8a2:	0f 1f 40 00          	nopl   0x0(%rax)
 8a6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 8ad:	00 00 00

00000000000008b0 <__do_global_dtors_aux>:
 8b0:	80 3d 6d 17 20 00 00 	cmpb   $0x0,0x20176d(%rip)        # 202024 <_edata>
 8b7:	75 27                	jne    8e0 <__do_global_dtors_aux+0x30>
 8b9:	48 83 3d 37 17 20 00 	cmpq   $0x0,0x201737(%rip)        # 201ff8 <_DYNAMIC+0x238>
 8c0:	00
 8c1:	55                   	push   %rbp
 8c2:	48 89 e5             	mov    %rsp,%rbp
 8c5:	74 0c                	je     8d3 <__do_global_dtors_aux+0x23>
 8c7:	48 8d 3d 82 14 20 00 	lea    0x201482(%rip),%rdi        # 201d50 <__dso_handle>
 8ce:	e8 0d ff ff ff       	callq  7e0 <puts@plt+0x10>
 8d3:	e8 48 ff ff ff       	callq  820 <deregister_tm_clones>
 8d8:	5d                   	pop    %rbp
 8d9:	c6 05 44 17 20 00 01 	movb   $0x1,0x201744(%rip)        # 202024 <_edata>
 8e0:	f3 c3                	repz retq
 8e2:	0f 1f 40 00          	nopl   0x0(%rax)
 8e6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 8ed:	00 00 00

00000000000008f0 <frame_dummy>:
 8f0:	48 8d 3d 51 14 20 00 	lea    0x201451(%rip),%rdi        # 201d48 <__JCR_END__>
 8f7:	48 83 3f 00          	cmpq   $0x0,(%rdi)
 8fb:	75 0b                	jne    908 <frame_dummy+0x18>
 8fd:	e9 5e ff ff ff       	jmpq   860 <register_tm_clones>
 902:	66 0f 1f 44 00 00    	nopw   0x0(%rax,%rax,1)
 908:	48 8b 05 c9 16 20 00 	mov    0x2016c9(%rip),%rax        # 201fd8 <_DYNAMIC+0x218>
 90f:	48 85 c0             	test   %rax,%rax
 912:	74 e9                	je     8fd <frame_dummy+0xd>
 914:	55                   	push   %rbp
 915:	48 89 e5             	mov    %rsp,%rbp
 918:	ff d0                	callq  *%rax
 91a:	5d                   	pop    %rbp
 91b:	e9 40 ff ff ff       	jmpq   860 <register_tm_clones>

0000000000000920 <func(Base&)>:
 920:	55                   	push   %rbp
 921:	48 89 e5             	mov    %rsp,%rbp
 924:	48 83 ec 10          	sub    $0x10,%rsp
 928:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 92c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 930:	48 8b 00             	mov    (%rax),%rax
 933:	48 8b 00             	mov    (%rax),%rax
 936:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
 93a:	48 89 d7             	mov    %rdx,%rdi
 93d:	ff d0                	callq  *%rax
 93f:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 943:	48 8b 00             	mov    (%rax),%rax
 946:	48 83 c0 08          	add    $0x8,%rax
 94a:	48 8b 00             	mov    (%rax),%rax
 94d:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
 951:	48 89 d7             	mov    %rdx,%rdi
 954:	ff d0                	callq  *%rax
 956:	90                   	nop
 957:	c9                   	leaveq
 958:	c3                   	retq

0000000000000959 <main>:
 959:	55                   	push   %rbp
 95a:	48 89 e5             	mov    %rsp,%rbp
 95d:	48 83 ec 10          	sub    $0x10,%rsp
 961:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
 965:	48 89 c7             	mov    %rax,%rdi
 968:	e8 75 00 00 00       	callq  9e2 <Derived::Derived()>
 96d:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
 971:	48 89 c7             	mov    %rax,%rdi
 974:	e8 a7 ff ff ff       	callq  920 <func(Base&)>
 979:	b8 00 00 00 00       	mov    $0x0,%eax
 97e:	c9                   	leaveq
 97f:	c3                   	retq

0000000000000980 <Base::Base()>:
 980:	55                   	push   %rbp
 981:	48 89 e5             	mov    %rsp,%rbp
 984:	48 83 ec 10          	sub    $0x10,%rsp
 988:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 98c:	48 8d 15 f5 13 20 00 	lea    0x2013f5(%rip),%rdx        # 201d88 <vtable for Base+0x10>
 993:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 997:	48 89 10             	mov    %rdx,(%rax)
 99a:	48 8d 3d 23 01 00 00 	lea    0x123(%rip),%rdi        # ac4 <_IO_stdin_used+0x4>
 9a1:	e8 2a fe ff ff       	callq  7d0 <puts@plt>
 9a6:	90                   	nop
 9a7:	c9                   	leaveq
 9a8:	c3                   	retq
 9a9:	90                   	nop

00000000000009aa <Base::hoge()>:
 9aa:	55                   	push   %rbp
 9ab:	48 89 e5             	mov    %rsp,%rbp
 9ae:	48 83 ec 10          	sub    $0x10,%rsp
 9b2:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 9b6:	48 8d 3d 1f 01 00 00 	lea    0x11f(%rip),%rdi        # adc <_IO_stdin_used+0x1c>
 9bd:	e8 0e fe ff ff       	callq  7d0 <puts@plt>
 9c2:	90                   	nop
 9c3:	c9                   	leaveq
 9c4:	c3                   	retq
 9c5:	90                   	nop

00000000000009c6 <Base::fuga()>:
 9c6:	55                   	push   %rbp
 9c7:	48 89 e5             	mov    %rsp,%rbp
 9ca:	48 83 ec 10          	sub    $0x10,%rsp
 9ce:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 9d2:	48 8d 3d 1b 01 00 00 	lea    0x11b(%rip),%rdi        # af4 <_IO_stdin_used+0x34>
 9d9:	e8 f2 fd ff ff       	callq  7d0 <puts@plt>
 9de:	90                   	nop
 9df:	c9                   	leaveq
 9e0:	c3                   	retq
 9e1:	90                   	nop

00000000000009e2 <Derived::Derived()>:
 9e2:	55                   	push   %rbp
 9e3:	48 89 e5             	mov    %rsp,%rbp
 9e6:	48 83 ec 10          	sub    $0x10,%rsp
 9ea:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 9ee:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 9f2:	48 89 c7             	mov    %rax,%rdi
 9f5:	e8 86 ff ff ff       	callq  980 <Base::Base()>
 9fa:	48 8d 15 67 13 20 00 	lea    0x201367(%rip),%rdx        # 201d68 <vtable for Derived+0x10>
 a01:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 a05:	48 89 10             	mov    %rdx,(%rax)
 a08:	48 8d 3d fd 00 00 00 	lea    0xfd(%rip),%rdi        # b0c <_IO_stdin_used+0x4c>
 a0f:	e8 bc fd ff ff       	callq  7d0 <puts@plt>
 a14:	90                   	nop
 a15:	c9                   	leaveq
 a16:	c3                   	retq
 a17:	90                   	nop

0000000000000a18 <Derived::fuga()>:
 a18:	55                   	push   %rbp
 a19:	48 89 e5             	mov    %rsp,%rbp
 a1c:	48 83 ec 10          	sub    $0x10,%rsp
 a20:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 a24:	48 8d 3d ff 00 00 00 	lea    0xff(%rip),%rdi        # b2a <_IO_stdin_used+0x6a>
 a2b:	e8 a0 fd ff ff       	callq  7d0 <puts@plt>
 a30:	90                   	nop
 a31:	c9                   	leaveq
 a32:	c3                   	retq
 a33:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 a3a:	00 00 00
 a3d:	0f 1f 00             	nopl   (%rax)

0000000000000a40 <__libc_csu_init>:
 a40:	41 57                	push   %r15
 a42:	41 56                	push   %r14
 a44:	41 89 ff             	mov    %edi,%r15d
 a47:	41 55                	push   %r13
 a49:	41 54                	push   %r12
 a4b:	4c 8d 25 e6 12 20 00 	lea    0x2012e6(%rip),%r12        # 201d38 <__frame_dummy_init_array_entry>
 a52:	55                   	push   %rbp
 a53:	48 8d 2d e6 12 20 00 	lea    0x2012e6(%rip),%rbp        # 201d40 <__init_array_end>
 a5a:	53                   	push   %rbx
 a5b:	49 89 f6             	mov    %rsi,%r14
 a5e:	49 89 d5             	mov    %rdx,%r13
 a61:	4c 29 e5             	sub    %r12,%rbp
 a64:	48 83 ec 08          	sub    $0x8,%rsp
 a68:	48 c1 fd 03          	sar    $0x3,%rbp
 a6c:	e8 2f fd ff ff       	callq  7a0 <_init>
 a71:	48 85 ed             	test   %rbp,%rbp
 a74:	74 20                	je     a96 <__libc_csu_init+0x56>
 a76:	31 db                	xor    %ebx,%ebx
 a78:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)
 a7f:	00
 a80:	4c 89 ea             	mov    %r13,%rdx
 a83:	4c 89 f6             	mov    %r14,%rsi
 a86:	44 89 ff             	mov    %r15d,%edi
 a89:	41 ff 14 dc          	callq  *(%r12,%rbx,8)
 a8d:	48 83 c3 01          	add    $0x1,%rbx
 a91:	48 39 dd             	cmp    %rbx,%rbp
 a94:	75 ea                	jne    a80 <__libc_csu_init+0x40>
 a96:	48 83 c4 08          	add    $0x8,%rsp
 a9a:	5b                   	pop    %rbx
 a9b:	5d                   	pop    %rbp
 a9c:	41 5c                	pop    %r12
 a9e:	41 5d                	pop    %r13
 aa0:	41 5e                	pop    %r14
 aa2:	41 5f                	pop    %r15
 aa4:	c3                   	retq
 aa5:	90                   	nop
 aa6:	66 2e 0f 1f 84 00 00 	nopw   %cs:0x0(%rax,%rax,1)
 aad:	00 00 00

0000000000000ab0 <__libc_csu_fini>:
 ab0:	f3 c3                	repz retq

Disassembly of section .fini:

0000000000000ab4 <_fini>:
 ab4:	48 83 ec 08          	sub    $0x8,%rsp
 ab8:	48 83 c4 08          	add    $0x8,%rsp
 abc:	c3                   	retq

先程宿題にした.plt.gotが逆アセンブルされている.xchg %ax,%axはNOPなので,*(%rip + 0x201812)にJMPするだけのコードのようだ.
以下,普通にビルドしたプログラムの逆アセンブル結果と比較してみる.量が多いのでDerived::Derived()だけ抜き出す.

普通にビルドした方.

0000000000400760 <Derived::Derived()>:
  400760:	55                   	push   %rbp
  400761:	48 89 e5             	mov    %rsp,%rbp
  400764:	48 83 ec 10          	sub    $0x10,%rsp
  400768:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  40076c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400770:	48 89 c7             	mov    %rax,%rdi
  400773:	e8 8e ff ff ff       	callq  400706 <Base::Base()>
  400778:	ba d8 08 40 00       	mov    $0x4008d8,%edx
  40077d:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400781:	48 89 10             	mov    %rdx,(%rax)
  400784:	bf 88 08 40 00       	mov    $0x400888,%edi
  400789:	e8 12 fe ff ff       	callq  4005a0 <puts@plt>
  40078e:	90                   	nop
  40078f:	c9                   	leaveq
  400790:	c3                   	retq
  400791:	90                   	nop

PIEの方.

00000000000009e2 <Derived::Derived()>:
 9e2:	55                   	push   %rbp
 9e3:	48 89 e5             	mov    %rsp,%rbp
 9e6:	48 83 ec 10          	sub    $0x10,%rsp
 9ea:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
 9ee:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 9f2:	48 89 c7             	mov    %rax,%rdi
 9f5:	e8 86 ff ff ff       	callq  980 <Base::Base()>
 9fa:	48 8d 15 67 13 20 00 	lea    0x201367(%rip),%rdx        # 201d68 <vtable for Derived+0x10>
 a01:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
 a05:	48 89 10             	mov    %rdx,(%rax)
 a08:	48 8d 3d fd 00 00 00 	lea    0xfd(%rip),%rdi        # b0c <_IO_stdin_used+0x4c>
 a0f:	e8 bc fd ff ff       	callq  7d0 <puts@plt>
 a14:	90                   	nop
 a15:	c9                   	leaveq
 a16:	c3                   	retq
 a17:	90                   	nop

見比べてみると,call命令はそのままアドレスが格納されているが,vtableのアドレスやputs()の引数(ここでputsが呼ばれているのはprintfが最適化されているため)は%ripから計算していることが分かる.普通にビルドした方はともかく,PIEの方もcall先のアドレスが固定(e.g. Base::Base() -> 0x980)になっているように見えるが,これはASLRが有効であれば実行時にランダムな値が足し合わさるようだ.
gdbで実行時のアドレスを確認してみる.gdbではASLRが無効化されるので,disable-randomizationオプションをoffにすることで有効化する.

$ gdb -ex 'set print asm-demangle on' -ex 'set disable-randomization off' vtable_pie
(gdb) break Derived::Derived()
Breakpoint 1 at 0x9e6
(gdb) run
Starting program: /home/vagrant/vtable_pie

Breakpoint 1, 0x000055e54d3719e6 in Derived::Derived() ()
(gdb) disassemble
Dump of assembler code for function _ZN7DerivedC2Ev:
   0x000055e54d3719e2 <+0>:	push   %rbp
   0x000055e54d3719e3 <+1>:	mov    %rsp,%rbp
=> 0x000055e54d3719e6 <+4>:	sub    $0x10,%rsp
   0x000055e54d3719ea <+8>:	mov    %rdi,-0x8(%rbp)
   0x000055e54d3719ee <+12>:	mov    -0x8(%rbp),%rax
   0x000055e54d3719f2 <+16>:	mov    %rax,%rdi
   0x000055e54d3719f5 <+19>:	callq  0x55e54d371980 <Base::Base()>
   0x000055e54d3719fa <+24>:	lea    0x201367(%rip),%rdx        # 0x55e54d572d68 <vtable for Derived+16>
   0x000055e54d371a01 <+31>:	mov    -0x8(%rbp),%rax
   0x000055e54d371a05 <+35>:	mov    %rdx,(%rax)
   0x000055e54d371a08 <+38>:	lea    0xfd(%rip),%rdi        # 0x55e54d371b0c
   0x000055e54d371a0f <+45>:	callq  0x55e54d3717d0 <puts@plt>
   0x000055e54d371a14 <+50>:	nop
   0x000055e54d371a15 <+51>:	leaveq
   0x000055e54d371a16 <+52>:	retq
End of assembler dump.
(gdb)

再度起動し,アドレス空間が変わっていることを確認する.

(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/vagrant/vtable_pie

Breakpoint 1, 0x000055dda61629e6 in Derived::Derived() ()
(gdb) disassemble
Dump of assembler code for function _ZN7DerivedC2Ev:
   0x000055dda61629e2 <+0>:	push   %rbp
   0x000055dda61629e3 <+1>:	mov    %rsp,%rbp
=> 0x000055dda61629e6 <+4>:	sub    $0x10,%rsp
   0x000055dda61629ea <+8>:	mov    %rdi,-0x8(%rbp)
   0x000055dda61629ee <+12>:	mov    -0x8(%rbp),%rax
   0x000055dda61629f2 <+16>:	mov    %rax,%rdi
   0x000055dda61629f5 <+19>:	callq  0x55dda6162980 <Base::Base()>
   0x000055dda61629fa <+24>:	lea    0x201367(%rip),%rdx        # 0x55dda6363d68 <vtable for Derived+16>
   0x000055dda6162a01 <+31>:	mov    -0x8(%rbp),%rax
   0x000055dda6162a05 <+35>:	mov    %rdx,(%rax)
   0x000055dda6162a08 <+38>:	lea    0xfd(%rip),%rdi        # 0x55dda6162b0c
   0x000055dda6162a0f <+45>:	callq  0x55dda61627d0 <puts@plt>
   0x000055dda6162a14 <+50>:	nop
   0x000055dda6162a15 <+51>:	leaveq
   0x000055dda6162a16 <+52>:	retq
End of assembler dump.
(gdb)

呼び出す関数(Base::Base(),puts@plt)のアドレスが変わっていることが分かる.
実行時にアドレスが足し合わされるといった説明を見たが,call命令のオペランドが実行時に機械語のレベルで書き換えられるのだろうか.それなら確かにパフォーマンス低下もうなずける.というかそれ無茶では…?
実際にこの時の機械語をチェックしてみよう.gdbのdisassemble命令に/rというオプションを付けるとマシン語の対応が分かる.

(gdb) disassemble /r
Dump of assembler code for function _ZN7DerivedC2Ev:
   0x000056083c3fc9e2 <+0>:	55	push   %rbp
   0x000056083c3fc9e3 <+1>:	48 89 e5	mov    %rsp,%rbp
=> 0x000056083c3fc9e6 <+4>:	48 83 ec 10	sub    $0x10,%rsp
   0x000056083c3fc9ea <+8>:	48 89 7d f8	mov    %rdi,-0x8(%rbp)
   0x000056083c3fc9ee <+12>:	48 8b 45 f8	mov    -0x8(%rbp),%rax
   0x000056083c3fc9f2 <+16>:	48 89 c7	mov    %rax,%rdi
   0x000056083c3fc9f5 <+19>:	e8 86 ff ff ff	callq  0x56083c3fc980 <Base::Base()>
   0x000056083c3fc9fa <+24>:	48 8d 15 67 13 20 00	lea    0x201367(%rip),%rdx        # 0x56083c5fdd68 <vtable for Derived+16>
   0x000056083c3fca01 <+31>:	48 8b 45 f8	mov    -0x8(%rbp),%rax
   0x000056083c3fca05 <+35>:	48 89 10	mov    %rdx,(%rax)
   0x000056083c3fca08 <+38>:	48 8d 3d fd 00 00 00	lea    0xfd(%rip),%rdi        # 0x56083c3fcb0c
   0x000056083c3fca0f <+45>:	e8 bc fd ff ff	callq  0x56083c3fc7d0 <puts@plt>
   0x000056083c3fca14 <+50>:	90	nop
   0x000056083c3fca15 <+51>:	c9	leaveq
   0x000056083c3fca16 <+52>:	c3	retq
End of assembler dump.

call命令だけ抜き出して,objdumpの逆アセンブル結果と見比べてみる.
gdb

0x000056083c3fc9f5 <+19>:	e8 86 ff ff ff	callq  0x56083c3fc980 <Base::Base()>

objdump

9f5:	e8 86 ff ff ff       	callq  980 <Base::Base()>

変わってない…?と思ったが,callqのOpcodeを見ると「Call near, relative, displacement relative to next instruction」とのことなので,%ripからの相対位置で呼び出しているようだ.
Liberation: x86 Instruction Set Reference
普通にビルドした方も見てみる.

400773:	e8 8e ff ff ff       	callq  400706 <Base::Base()>

同じくOpcodeがE8なので,おそらくここでパフォーマンス低下は招いていないだろう.

これらの結果より,PIEでない場合は即値で扱っていたアドレスがlea命令に置き換わったことで,ある程度のパフォーマンス低下が起きていると考えられる.また,ローダの処理が追加されていることもパフォーマンス低下を招いていると予想できる.これ以上はちゃんとサーベイしないと分からないので,もっと論文読んで出直します.

今回の内容マサカリビリティに溢れているので怖いっすね

参考資料:
OSセキュリティチュートリアル
PIE (位置独立実行形式) を作成する - bkブログ
Linux の共有ライブラリを作るとき PIC でコンパイルするのはなぜか - bkブログ
革命の日々! PIE(位置独立実行ファイル)で遊んでみる

vtableの中身を見てみる

C++ポリモーフィズムを実現するためにvtableと呼ばれる機構が用いられている.
だいたいの入門本でvtableという言葉は出てくるものの,実装については特に触れられていないので中身を見てみた.

検証用プログラムには以下を用いた.また,実行環境はGNU/Linux Fedora 25を用い,コンパイラgcc 6.4.1を用いた.

#include <cstdio>

class Base {
public:
    Base() { printf("Base::Base() has called\n"); }
    virtual void hoge() { printf("Base::hoge() has called\n"); }
    virtual void fuga() { printf("Base::fuga() has called\n"); }
};

class Derived : public Base {
public:
    Derived() { printf("Derived::Derived() has called\n"); }
    void fuga() { printf("Derived::fuga() has called\n"); }
};

void func(Base &x)
{
    x.hoge();
    x.fuga();
}

int main()
{
    Derived obj;
    func(obj);

    return 0;
}

コンパイルして実行すると以下のようになる.

$ g++ vtable.cpp
$ ./a.out
Base::Base() has called
Derived::Derived() has called
Base::hoge() has called
Derived::fuga() has called

ではobjdumpで逆アセンブルしてみて中身を見てみよう.

$ objdump -d a.out | c++filt

a.out:     file format elf64-x86-64

〜
Disassembly of section .text:
〜
00000000004006a6 <func(Base&)>:
  4006a6:	55                   	push   %rbp
  4006a7:	48 89 e5             	mov    %rsp,%rbp
  4006aa:	48 83 ec 10          	sub    $0x10,%rsp
  4006ae:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  4006b2:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  4006b6:	48 8b 00             	mov    (%rax),%rax
  4006b9:	48 8b 00             	mov    (%rax),%rax
  4006bc:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  4006c0:	48 89 d7             	mov    %rdx,%rdi
  4006c3:	ff d0                	callq  *%rax
  4006c5:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  4006c9:	48 8b 00             	mov    (%rax),%rax
  4006cc:	48 83 c0 08          	add    $0x8,%rax
  4006d0:	48 8b 00             	mov    (%rax),%rax
  4006d3:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  4006d7:	48 89 d7             	mov    %rdx,%rdi
  4006da:	ff d0                	callq  *%rax
  4006dc:	90                   	nop
  4006dd:	c9                   	leaveq
  4006de:	c3                   	retq

00000000004006df <main>:
  4006df:	55                   	push   %rbp
  4006e0:	48 89 e5             	mov    %rsp,%rbp
  4006e3:	48 83 ec 10          	sub    $0x10,%rsp
  4006e7:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
  4006eb:	48 89 c7             	mov    %rax,%rdi
  4006ee:	e8 6d 00 00 00       	callq  400760 <Derived::Derived()>
  4006f3:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
  4006f7:	48 89 c7             	mov    %rax,%rdi
  4006fa:	e8 a7 ff ff ff       	callq  4006a6 <func(Base&)>
  4006ff:	b8 00 00 00 00       	mov    $0x0,%eax
  400704:	c9                   	leaveq
  400705:	c3                   	retq

0000000000400706 <Base::Base()>:
  400706:	55                   	push   %rbp
  400707:	48 89 e5             	mov    %rsp,%rbp
  40070a:	48 83 ec 10          	sub    $0x10,%rsp
  40070e:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  400712:	ba f8 08 40 00       	mov    $0x4008f8,%edx
  400717:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  40071b:	48 89 10             	mov    %rdx,(%rax)
  40071e:	bf 40 08 40 00       	mov    $0x400840,%edi
  400723:	e8 78 fe ff ff       	callq  4005a0 <puts@plt>
  400728:	90                   	nop
  400729:	c9                   	leaveq
  40072a:	c3                   	retq
  40072b:	90                   	nop

000000000040072c <Base::hoge()>:
  40072c:	55                   	push   %rbp
  40072d:	48 89 e5             	mov    %rsp,%rbp
  400730:	48 83 ec 10          	sub    $0x10,%rsp
  400734:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  400738:	bf 58 08 40 00       	mov    $0x400858,%edi
  40073d:	e8 5e fe ff ff       	callq  4005a0 <puts@plt>
  400742:	90                   	nop
  400743:	c9                   	leaveq
  400744:	c3                   	retq
  400745:	90                   	nop

0000000000400746 <Base::fuga()>:
  400746:	55                   	push   %rbp
  400747:	48 89 e5             	mov    %rsp,%rbp
  40074a:	48 83 ec 10          	sub    $0x10,%rsp
  40074e:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  400752:	bf 70 08 40 00       	mov    $0x400870,%edi
  400757:	e8 44 fe ff ff       	callq  4005a0 <puts@plt>
  40075c:	90                   	nop
  40075d:	c9                   	leaveq
  40075e:	c3                   	retq
  40075f:	90                   	nop

0000000000400760 <Derived::Derived()>:
  400760:	55                   	push   %rbp
  400761:	48 89 e5             	mov    %rsp,%rbp
  400764:	48 83 ec 10          	sub    $0x10,%rsp
  400768:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  40076c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400770:	48 89 c7             	mov    %rax,%rdi
  400773:	e8 8e ff ff ff       	callq  400706 <Base::Base()>
  400778:	ba d8 08 40 00       	mov    $0x4008d8,%edx
  40077d:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400781:	48 89 10             	mov    %rdx,(%rax)
  400784:	bf 88 08 40 00       	mov    $0x400888,%edi
  400789:	e8 12 fe ff ff       	callq  4005a0 <puts@plt>
  40078e:	90                   	nop
  40078f:	c9                   	leaveq
  400790:	c3                   	retq
  400791:	90                   	nop

0000000000400792 <Derived::fuga()>:
  400792:	55                   	push   %rbp
  400793:	48 89 e5             	mov    %rsp,%rbp
  400796:	48 83 ec 10          	sub    $0x10,%rsp
  40079a:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
  40079e:	bf a6 08 40 00       	mov    $0x4008a6,%edi
  4007a3:	e8 f8 fd ff ff       	callq  4005a0 <puts@plt>
  4007a8:	90                   	nop
  4007a9:	c9                   	leaveq
  4007aa:	c3                   	retq
  4007ab:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)

まずはfunc()から見てみる.

00000000004006a6 <func(Base&)>:
# スタックを掘る
  4006a6:	55                   	push   %rbp
  4006a7:	48 89 e5             	mov    %rsp,%rbp
  4006aa:	48 83 ec 10          	sub    $0x10,%rsp

# Linux x86_64なのでrdiには1番目の引数が入る
# メモリの(rbp-0x8)番地に1番目の引数(つまりBase &x)を格納
  4006ae:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
# メモリからraxにコピー
  4006b2:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
# raxの指すメモリを2回デリファレンス(rax=*rax)
  4006b6:	48 8b 00             	mov    (%rax),%rax
  4006b9:	48 8b 00             	mov    (%rax),%rax
# rdxにメモリから&xをコピーし,更にrdiにコピー
  4006bc:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  4006c0:	48 89 d7             	mov    %rdx,%rdi
# raxの指す関数をcall
  4006c3:	ff d0                	callq  *%rax
# もう一度メモリから&xをコピーし,raxに格納
  4006c5:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
# 今度はraxの指すメモリをデリファレンスし,そのアドレスに0x8を加える
  4006c9:	48 8b 00             	mov    (%rax),%rax
  4006cc:	48 83 c0 08          	add    $0x8,%rax
# そしてそのアドレスをデリファレンス
  4006d0:	48 8b 00             	mov    (%rax),%rax
# rdxにメモリから&xをコピーし,更にrdiにコピー
  4006d3:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  4006d7:	48 89 d7             	mov    %rdx,%rdi
# raxの指す関数をcall
  4006da:	ff d0                	callq  *%rax
# 終了処理
  4006dc:	90                   	nop
  4006dd:	c9                   	leaveq
  4006de:	c3                   	retq

func()にはオブジェクトのアドレスが格納されているので,オブジェクトの先頭位置を一度デリファレンスし,その情報を元に呼び出す関数を決定している.
では,オブジェクトの先頭に何が格納されているかを確認する.オブジェクトの初期化部分から見ていこう.

00000000004006df <main>:
# スタックを掘る
  4006df:	55                   	push   %rbp
  4006e0:	48 89 e5             	mov    %rsp,%rbp
  4006e3:	48 83 ec 10          	sub    $0x10,%rsp
# スタックの(rbp-0x8)のアドレスをraxに格納し,それをrdiにコピー
  4006e7:	48 8d 45 f8          	lea    -0x8(%rbp),%rax
  4006eb:	48 89 c7             	mov    %rax,%rdi
# コンストラクタの呼び出し
  4006ee:	e8 6d 00 00 00       	callq  400760 <Derived::Derived()>
〜

スタック上にメモリを確保し,それをrdi経由でDerived::Derived()に渡していることが分かる.
では,Derived::Derived()を見ていこう.

0000000000400760 <Derived::Derived()>:
# スタックを掘る
  400760:	55                   	push   %rbp
  400761:	48 89 e5             	mov    %rsp,%rbp
  400764:	48 83 ec 10          	sub    $0x10,%rsp
# 引数をメモリにコピー
  400768:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
# メモリから引数をraxにコピーし,rdiにコピー
  40076c:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
  400770:	48 89 c7             	mov    %rax,%rdi
# Base::Base()の呼び出し
  400773:	e8 8e ff ff ff       	callq  400706 <Base::Base()>
# edxに即値を代入
  400778:	ba d8 08 40 00       	mov    $0x4008d8,%edx
# メモリから引数をraxにコピー
  40077d:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
# 先程の即値をraxの参照先にコピー
  400781:	48 89 10             	mov    %rdx,(%rax)
# ediに即値を代入(おそらく文字列のアドレスだろう)
  400784:	bf 88 08 40 00       	mov    $0x400888,%edi
# puts@pltを呼び出し
  400789:	e8 12 fe ff ff       	callq  4005a0 <puts@plt>
# 終了処理
  40078e:	90                   	nop
  40078f:	c9                   	leaveq
  400790:	c3                   	retq
  400791:	90                   	nop

突然出てきた即値も気になるが,まずはBase::Base()を見ていこう.

0000000000400706 <Base::Base()>:
# スタックを掘る
  400706:	55                   	push   %rbp
  400707:	48 89 e5             	mov    %rsp,%rbp
  40070a:	48 83 ec 10          	sub    $0x10,%rsp
# 引数をメモリにコピー
  40070e:	48 89 7d f8          	mov    %rdi,-0x8(%rbp)
# 即値をedxに代入
  400712:	ba f8 08 40 00       	mov    $0x4008f8,%edx
# メモリから引数をraxにコピー
  400717:	48 8b 45 f8          	mov    -0x8(%rbp),%rax
# 先程の即値をraxの参照先にコピー
  40071b:	48 89 10             	mov    %rdx,(%rax)
# ediに即値を代入(おそらく文字列のアドレスだろう)
  40071e:	bf 40 08 40 00       	mov    $0x400840,%edi
# puts@pltを呼び出し
  400723:	e8 78 fe ff ff       	callq  4005a0 <puts@plt>
# 終了処理
  400728:	90                   	nop
  400729:	c9                   	leaveq
  40072a:	c3                   	retq
  40072b:	90                   	nop

こちらでも同じく即値を引数の参照先にコピーしている.コンストラクタに渡される引数は未初期化オブジェクトのアドレスである.main()でスタックから確保したアドレスが格納され,その先頭位置に即値が代入されている.そして,Base::Base()で設定した$0x4008f8という値は,Derived::Derived()の以降の処理で$0x4008d8に上書きされている.

くどいようだが,もう一度C++ソースコードからコンストラクタの定義を見てみよう.

class Base {
public:
    Base() { printf("Base::Base() has called\n"); }
    virtual void hoge() { printf("Base::hoge() has called\n"); }
    virtual void fuga() { printf("Base::fuga() has called\n"); }
};

class Derived : public Base {
public:
    Derived() { printf("Derived::Derived() has called\n"); }
    void fuga() { printf("Derived::fuga() has called\n"); }
};

中では特に代入処理はしていない.では,Derived::Derived()の$0x4008d8とBase::Base()の$0x4008f8とは何なのだろうか.
この実行時のアドレスが位置するセクションが知りたいので,readelfコマンドでセクションヘッダを見てみよう.
(追記)今回は単一のリンク済み実行ファイルを見ているのでセクションヘッダの位置が*たまたま*実行時のアドレスに対応しています.実行時のアドレスに対応するファイル位置を確認したいのであれば,本来はプログラムヘッダを見なければなりません.(誰もこんな記事見ないと思いますが)以下の文章は間違っているので気をつけてください.

$ readelf -S a.out
There are 30 section headers, starting at offset 0x1c48:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
〜
  [15] .rodata           PROGBITS         0000000000400830  00000830
       0000000000000116  0000000000000000   A       0     0     8
〜
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

0x400830+0x116の範囲は.rodataであることが分かった.つまり,$0x4008d8と$0x4008f8は(ついでにputs@pltの前にあった$0x400888と$0x400840も).rodataに存在する.
readelfコマンドで.rodataをダンプして中身を見てみよう.

$ readelf -x .rodata a.out

Hex dump of section '.rodata':
  0x00400830 01000200 00000000 00000000 00000000 ................
  0x00400840 42617365 3a3a4261 73652829 20686173 Base::Base() has
  0x00400850 2063616c 6c656400 42617365 3a3a686f  called.Base::ho
  0x00400860 67652829 20686173 2063616c 6c656400 ge() has called.
  0x00400870 42617365 3a3a6675 67612829 20686173 Base::fuga() has
  0x00400880 2063616c 6c656400 44657269 7665643a  called.Derived:
  0x00400890 3a446572 69766564 28292068 61732063 :Derived() has c
  0x004008a0 616c6c65 64004465 72697665 643a3a66 alled.Derived::f
  0x004008b0 75676128 29206861 73206361 6c6c6564 uga() has called
  0x004008c0 00000000 00000000 00000000 00000000 ................
  0x004008d0 08094000 00000000 2c074000 00000000 ..@.....,.@.....
  0x004008e0 92074000 00000000 00000000 00000000 ..@.............
  0x004008f0 30094000 00000000 2c074000 00000000 0.@.....,.@.....
  0x00400900 46074000 00000000 90106000 00000000 F.@.......`.....
  0x00400910 20094000 00000000 30094000 00000000  .@.....0.@.....
  0x00400920 37446572 69766564 00000000 00000000 7Derived........
  0x00400930 38106000 00000000 40094000 00000000 8.`.....@.@.....
  0x00400940 34426173 6500                       4Base.

とりあえず文字列が存在することは分かったが,見づらいので先程のreadelf -Sのアドレス情報とオフセット情報を元に,odで$0x4008d8と$0x4008f8に該当するファイル位置をダンプしてみる..rodataの開始アドレスである0x400830のオフセットは0x830であるため,実行時に0x4008d8に展開される部分は単純にファイルの0x8d8バイト目でよさそうだ.

$ od -j 0x8d8 -N 16 -tx8 -Ax a.out
0008d8 000000000040072c 0000000000400792
0008e8
$ od -j 0x8f8 -N 16 -tx8 -Ax a.out
0008f8 000000000040072c 0000000000400746
000908

何かのアドレスらしき値が格納されていることが分かる.ここで,再度objdumpの逆アセンブル結果を見てみよう.

000000000040072c <Base::hoge()>:
〜

0000000000400746 <Base::fuga()>:
〜

0000000000400792 <Derived::fuga()>:
〜

関数アドレスがodのダンプ結果と一致することが分かる.以上より,Base::Base()で格納した0x4008f8という値を元に,*0x4008f8にBase::hoge(),*(0x4008f8+0x8)にBase::fuga()のアドレスが格納されていることが分かった.また,Derived::Derived()で格納した0x4008d8という値を元に,*0x4008d8の位置にBase::hoge(),*(0x4008d8+0x8)ではオーバーライドしたDerived::fuga()のアドレスが格納されていることが分かった.

以上の機構により,C++ではポリモーフィズムを実現している.そして.rodataに存在する関数のアドレスのリストをvtableと呼んでいる.
.rodataにvtableを設置し各オブジェクトで二回デリファレンスするより,各オブジェクトの先頭に直接関数のアドレスのリストを格納すればデリファレンスの回数が減ってよいのでは?と思ったが,おそらくオブジェクトの数が多くなった場合にプログラムが無駄に肥大化するためこのような形になっているのだと思う.ちなみに,コンパイラの最適化レベルを上げるとまた異なった結果が得られるため,その辺りは注意されたい.

以下,参考サイト等.
以下のサイトではC++擬似コードで説明してくれているのでより分かりやすい.
qiita.com
stackoverflow.com

また,ヒープオーバーフローを用いてvtableを上書きする攻撃も存在するらしい.
inaz2.hatenablog.com

プログラミング言語が違うと実行バイナリも違うのか検証した話

こんな話が流れてきたので検証してみる.

とりあえず実行バイナリを直接吐き出す言語として,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");
}

C++

#include <cstdio>

int main()
{
	printf("Hello, World!\n");
	return 0;
}

Go(wikipediaから拝借)

package main

import "fmt"

func main() {
	fmt.Print("Hello, World!\n")
}

Haskell

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
qiita.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

Linux Kernelのパケット送信を追う(ソケット作成編)

実績解除のためにsendto(2)のシステムコール発行からe1000ドライバの送信処理までを追いかけてみた.取り急ぎソケット作成までをまとめてみる.
なお,Linux Kernelは4.14.0-rc8を対象としている.

初めに断っておくとアホほど長い上に備忘録なのでほとんど解説はしていない.もし間違えていたらごめんなさい.

続きを読む

Hello, World!するアセンブリ

x86_64向けに動作するHello, World!を書いた.長いので続きからにする.

続きを読む

MacでLinux Kernelをcloneするといじってないファイルがmodifiedされていると言われる現象

閲覧用にMac上にLinux Kernelをcloneしたところ、いじってないファイルがmodifiedであると言われて何もできなくなる事態が発生した。
具体的には以下のようなエラーメッセージが出力される。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   include/uapi/linux/netfilter/xt_CONNMARK.h
	modified:   include/uapi/linux/netfilter/xt_DSCP.h
	modified:   include/uapi/linux/netfilter/xt_MARK.h
	modified:   include/uapi/linux/netfilter/xt_RATEEST.h
	modified:   include/uapi/linux/netfilter/xt_TCPMSS.h
	modified:   include/uapi/linux/netfilter_ipv4/ipt_ECN.h
	modified:   include/uapi/linux/netfilter_ipv4/ipt_TTL.h
	modified:   include/uapi/linux/netfilter_ipv6/ip6t_HL.h
	modified:   net/netfilter/xt_DSCP.c
	modified:   net/netfilter/xt_HL.c
	modified:   net/netfilter/xt_RATEEST.c
	modified:   net/netfilter/xt_TCPMSS.c

検索したところ、以下のようなページが見つかった。
git - Linux Kernel sources modified on OSX right after clone - Stack Overflow

要するにHFS+ではファイル名の大文字と小文字の区別がつかないので、上のファイルは衝突してgitが混乱するというのが原因らしい。
実際に確認してみたところ、確かにinclude/uapi/linux/netfilter/xt_CONNMARK.hとinclude/uapi/linux/netfilter/xt_connmark.hというファイルが見つかった。

解決策は大文字と小文字が区別される専用のディスクイメージを作成し、その中でcloneするのが良いとのこと。ディスクユーティリティを開いて、ファイル→新規イメージ→空のイメージを作成から、フォーマットの欄で大文字/小文字を区別にすればcase-sensitiveなディスクイメージが作成できる。
結構回りくどいような…
回答者も言及しているが、Virtual Box等でVMを立ててLinux環境を作り、ssh越しに閲覧するのが早いと思う。emacs派ならTRAMPを使おう。

よく使うEmacs拡張

弊校では学部1年生からEmacsの使用が強制されており、習得度の低い学生が素のEmacsを使わされています。その結果、非常に残念なことにEmacsはただただ不便なだけのエディタとしてその名が知られています。そこで、弊校におけるEmacsの悪印象を払拭し、Emacsは便利なエディタであると布教するために僕がよく使っているEmacs拡張とその設定を挙げていきたいと思います。

続きを読む