c++でhello worldを書いてみた
TRANSCRIPT
C++ で Hello world を書いてみた
2015/5/17hotpepsi
自己紹介 twitter: @hotpepsi 趣味 : 競技プログラミング、
テニス、ピアノ キャッチフレーズ :
bootstrap から Bootstrap まで
デモ Ubuntu 14.04 x86-64
$ make -f hello_gen.mkg++ -fno-operator-names -o hello_gen_gen hello_gen_gen.cc./hello_gen_gen > numbers.ccg++ -o hello_gen hello_gen.cc./hello_gen > _.ccc -c -Wa,-R -w _.cld _.o -e _ -o hello./helloHello, world!
ソースコードの先頭部分
_[]={'('-'!'|((','-' ')<<('$'-' '))|(('$'-' '|(('$'-' ')<<('$'-' ')))<<('('-' '))|(('$'-' '|(('#'-'!')<<('$'-' '))|((('/'-' ')<<('$'-' '))<<('('-' ')))<<('='-'-')),...
記号プログラミング ( 二番煎じ )
C で記号プログラミング ( 復習 )
記号のみでソースコードを記述 int 型の配列 _ を定義して、 text セグメント
に配置してプログラムとして実行する エントリポイントを _start ではなく _ にする
ことでアルファベット (main) を書くことを回避
というような記号だけのソースコードを生成するジェネレータを (C で ) 書いた
強化点のご紹介
logo generator: http://www.schoolidolproject.com/
強化点 1 2010: C 2015: C++ ← New!
マクロがテンプレートに 2010: マクロ
MAKE32(_4,_8,_6,_5,_6,_C,_6,_C), // Hell 2015: テンプレート
Symbolizer<‘H’>().s // Hマクロは地獄
struct Symbolizer Symbolizer<0>().s -> !””
文字列 ( ポインタ ) の否定はゼロ Symbolizer<1>().s -> !!””
ゼロの否定は 1 Symbolizer<n>().s -> Sym
bolizer<(n & 1)>().s | (Symbolizer<(n>>1)>().s << 1)
テンプレートの優位性 マクロ :
0 から 16 までの値を用意する必要があった テンプレート :
特殊化により、最低限 0, 1, n を用意すれば、任意の数値が生成できる
例 : コンパイラにフィボナッチ数を計算させる 0 の部分を生成しないようにしたり、 2, 3, 4,
... と個別に用意すれば、コードがコンパクトに
強化点 2 2010: バイナリ (0xB8, 0x04, ...) 2015: ニーモニック (mov ...)
C++ でバイナリ生成といえば ... Xbyak
ニーモニックによる記述が可能に 可読性と保守性の大幅な向上
2010: MAKE32(_B,_8,_0,_4,_0,_0,_0,_0)
2015: mov(eax, 4);
コード生成のステップ
1. コードジェネレータジェネレータ (C++) Xbyak でバイナリ生成 バイナリを Symbolizer の呼び出しに置換
して出力2. コードジェネレータ (C++)
Symbolizer により記号化して出力3. 記号だけのソースコード (C)
強化点 3 2010: x86 2015: x86 + x86-64
x86-64 対応 (1) 32bit/64bit の両方に対応したいが、 #i
fdef は減らしたい↓
同じバイト列を生成するようにする
x86-64 対応 (2) x86 と x86-64 の命令バイト列はほぼ共
通 64bit の即値代入命令を使い、後半 32b
it に jmp 命令を埋め込むことで、 32bitモードの時だけ分岐する
これにより同じバイト列で両方に対応 デュアルアーキテクチャ
x86 と x64 の両方で動くシェルコードを書いてみるhttp://inaz2.hatenablog.com/entry/2014/07/06/185125
objdump: x86
$ objdump -d hello
hello: file format elf32-i386
Disassembly of section .text:
08048080 <_>: 8048080: c7 44 24 f0 48 65 6c 6c c7 44 24 f4 6f 2c 20 77 .D$.Hell.D$.o, w 8048090: c7 44 24 f8 6f 72 6c 64 66 c7 44 24 fc 21 0a ba .D$.orldf.D$.!.. 80480a0: 0e 00 00 00 48 bb 01 00 00 00 eb 19 00 00 48 8d ....H.........H. 80480b0: 74 24 f0 bf 01 00 00 00 89 f8 0f 05 31 ff b8 3c t$..........1..< 80480c0: 00 00 00 0f 05 8d 4c 24 f0 b8 04 00 00 00 cd 80 ......L$........ 80480d0: 31 db b8 01 00 00 00 cd 80 00 00 00 1...........
objdump: x86-64
$ objdump -d hello
hello: file format elf64-x86-64
Disassembly of section .text:
08048080 <_>: 8048080: c7 44 24 f0 48 65 6c 6c c7 44 24 f4 6f 2c 20 77 .D$.Hell.D$.o, w 8048090: c7 44 24 f8 6f 72 6c 64 66 c7 44 24 fc 21 0a ba .D$.orldf.D$.!.. 80480a0: 0e 00 00 00 48 bb 01 00 00 00 eb 19 00 00 48 8d ....H.........H. 80480b0: 74 24 f0 bf 01 00 00 00 89 f8 0f 05 31 ff b8 3c t$..........1..< 80480c0: 00 00 00 0f 05 8d 4c 24 f0 b8 04 00 00 00 cd 80 ......L$........ 80480d0: 31 db b8 01 00 00 00 cd 80 00 00 00 1...........
完全に一致
ファイルフォーマット以外は完全に一致
コードジェネレータジェネレータ
PutString(const std::string &message) {unsigned int *data = (unsigned int *)message.data();mov(dword[esp - 16], data[0]);mov(dword[esp - 12], data[1]);mov(dword[esp - 8], data[2]);mov(word[esp - 4], data[3]);mov(edx, message.length());dec(eax);mov(ebx, 1);jmp("@f");add(byte[eax], al);dec(eax);lea(esi, ptr[esp - 16]);mov(edi, 1);mov(eax, edi);syscall();xor(edi, edi);mov(eax, 60);syscall();
L("@@");lea(ecx, ptr[esp - 16]);mov(eax, 4);int80h();xor(ebx, ebx);mov(eax, 1);int80h();
}
スタック上にメッセージを置く
(x86/x86-64 共通 )
x86-64 用コード
x86 用コード
https://github.com/firewood/test/blob/master/hello_gen_gen.cc
ソースコード_[]={'('-'!'|((','-' ')<<('$'-' '))|
(('$'-' '|(('$'-' ')<<('$'-' ')))<<('('-' '))|
(('$'-' '|(('#'-'!')<<('$'-' '))|
((('/'-' ')<<('$'-' '))<<('('-' ')))<<('='-'-')),... F0
24
C744
mov dword ptr[esp-16], imm