tiny hello

36
Tiny hello

Upload: ezra

Post on 20-Feb-2016

51 views

Category:

Documents


0 download

DESCRIPTION

Tiny hello. h ello world. hello.c をコンパイルする。 % gcc -o hello hello.c ファイルサイズを確認。 % wc -c hello 4507 hello つまり、 4.5 KB くらいある。 hello world だけで 何故 そんな に大きいのか?. h ello world. hello.c をコンパイルする。 % gcc -o hello hello.c ファイルサイズを確認。 % wc -c hello 4507 hello - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Tiny hello

Tiny hello

Page 2: Tiny hello

hello worldhello.c をコンパイルする。

%gcc -o hello hello.c

ファイルサイズを確認。

%wc -c hello4507 hello

つまり、 4.5 KB くらいある。 hello world だけで何故そんなに大きいのか?

Page 3: Tiny hello

hello worldhello.c をコンパイルする。

%gcc -o hello hello.c

ファイルサイズを確認。

%wc -c hello4507 hello

つまり、 4.5 KB くらいある。 hello world だけで何故そんなに大きいのか?=> gcc -v で何をしているのかチェック

Page 4: Tiny hello

gcc -v%gcc -v hello.c -o hello.oUsing built-in specs.Target: i486-linux-gnuConfigured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-targets=all --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnuThread model: posixgcc version 4.4.5 (Debian 4.4.5-8) COLLECT_GCC_OPTIONS='-v' '-o' 'hello.o' '-mtune=generic' '-march=i586' /usr/lib/gcc/i486-linux-gnu/4.4.5/cc1 -quiet -v hello.c -quiet -dumpbase hello.c -mtune=generic -march=i586 -auxbase hello -version -o /tmp/ccIK75de.signoring nonexistent directory "/usr/local/include/i486-linux-gnu"ignoring nonexistent directory "/usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../i486-linux-gnu/include"ignoring nonexistent directory "/usr/include/i486-linux-gnu"#include "..." search starts here:#include <...> search starts here: /usr/local/include /usr/lib/gcc/i486-linux-gnu/4.4.5/include /usr/lib/gcc/i486-linux-gnu/4.4.5/include-fixed /usr/includeEnd of search list.GNU C (Debian 4.4.5-8) version 4.4.5 (i486-linux-gnu) compiled by GNU C version 4.4.5, GMP version 4.3.2, MPFR version 3.0.0-p3.GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072Compiler executable checksum: 0192d925385d4e6642a93c63f245f907COLLECT_GCC_OPTIONS='-v' '-o' 'hello.o' '-mtune=generic' '-march=i586' as -V -Qy -o /tmp/cczJa9u6.o /tmp/ccIK75de.sGNU assembler version 2.20.1 (i486-linux-gnu) using BFD version (GNU Binutils for Debian) 2.20.1-system.20100303COMPILER_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/LIBRARY_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/4.4.5/:/usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/i486-linux-gnu/4.4.5/../../../:/lib/:/usr/lib/COLLECT_GCC_OPTIONS='-v' '-o' 'hello.o' '-mtune=generic' '-march=i586' /usr/lib/gcc/i486-linux-gnu/4.4.5/collect2 --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o hello.o /usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.4.5/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.4.5 -L/usr/lib/gcc/i486-linux-gnu/4.4.5 -L/usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.5/../../.. /tmp/cczJa9u6.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.4.5/crtend.o /usr/lib/gcc/i486-linux-gnu/4.4.5/../../../../lib/crtn.o

Page 5: Tiny hello

リンクされているオブジェクト

リンクする際に様々な *.o ファイルをリンクしている。ldd で確認すると

%ldd hello linux-gate.so.1 => (0xb77ca000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7668000) /lib/ld-linux.so.2 (0xb77cb000)

な感じでリンクしているが、 *.o は glibc (libc.so) の初期化コードを含んでいて、これが原因でサイズが大きい。

以下、余計なオブジェクト一覧。

* crtbegin.o* crt1.o* crti.o* crtend.o* crtn.o

Page 6: Tiny hello

write 関数を呼べば?hello_write.c をビルド。

#include <unistd.h>

int main(void){ /* fd=1 だと stdout */ write(1,"Hello World\n",12); return 0;}

%gcc -o hello_write hello_write.c%./hello_write

ちゃんと動く。

Page 7: Tiny hello

write 関数を呼べば?

%ldd hello_write linux-gate.so.1 => (0xb7863000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7701000) /lib/ld-linux.so.2 (0xb7864000)

%wc -c hello_write4534 hello_write

な感じでサイズは小さくなっていない。

Page 8: Tiny hello

write 関数を呼べば?

%ldd hello_write linux-gate.so.1 => (0xb7863000) libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7701000) /lib/ld-linux.so.2 (0xb7864000)

%wc -c hello_write4534 hello_write

な感じでサイズは小さくなっていない。=>glibc の write を呼んでいる。

Page 9: Tiny hello

カーネルのシステムコールを直接叩く

www.kernel.org から kernel source を適当に取ってくる。が、現行のコードだとソースの構造が割と変わっていて良く分からないので、 binary hacks に書かれている通りの構造になっている version を適当に。今回は linux-2.6.18.8 を取ってきた。# 本当は現行 version のでやりたかったのだが、ソースを追い切れず。# 根性無しでごめんなさい…。

ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.18.8.tar.bz2から wget

Page 10: Tiny hello

カーネルのシステムコールを直接叩く

動かしたいコードは

write(1,"Hello World\n",12);

なので、

linux-2.6.18.8/include/asm-i386/unistd.h

を覗いて、 write 関数っぽいのを探す。

Page 11: Tiny hello

カーネルのシステムコールを直接叩く

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \type name(type1 arg1,type2 arg2,type3 arg3) \{ \long __res; \__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)) : "memory"); \__syscall_return(type,__res); \}

を入れれば動きそう。

Page 12: Tiny hello

カーネルのシステムコールを直接叩く

__syscall_return というマクロを中で使っているので、これの定義も取ってきて、

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \type name(type1 arg1,type2 arg2,type3 arg3) \{ \long __res; \__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)) : "memory"); \__syscall_return(type,__res); \}

void hello() { write(1, "Hello World\n", 12);}

な感じ。

Page 13: Tiny hello

カーネルのシステムコールを直接叩く

これを以下のようにコンパイル & リンクする。

%gcc -m32 -Os -fno-builtin -fomit-frame-pointer -fno-ident -c tiny_hello.c%ld -m elf_i386 --entry=hello -o hello tiny_hello.o

Page 14: Tiny hello

カーネルのシステムコールを直接叩く

これを以下のようにコンパイル & リンクする。

%gcc -m32 -Os -fno-builtin -fomit-frame-pointer -fno-ident -c tiny_hello.c%ld -m elf_i386 --entry=hello -o hello tiny_hello.o

だが、これだと SEGV で落ちる。普段は glibc がやってくれているが、今回は自分で exit() しなければならない。そこで、 exit() も動かす。

Page 15: Tiny hello

カーネルのシステムコールを直接叩く

なので、

#define _syscall1(type,name,type1,arg1) \type name(type1 arg1) \{ \long __res; \__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)) : "memory"); \__syscall_return(type,__res); \}

inline _syscall1(int, exit, int, status);

も追加しなければならない。

Page 16: Tiny hello

カーネルのシステムコールを直接叩く

結局、コードは

#include <asm/unistd.h>#define __syscall_return(type, res) (type)(res)

#define _syscall1(type,name,type1,arg1) \type name(type1 arg1) \{ \long __res; \__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)) : "memory"); \__syscall_return(type,__res); \}

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \type name(type1 arg1,type2 arg2,type3 arg3) \{ \long __res; \__asm__ volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ : "=a" (__res) \ : "0" (__NR_##name),"ri" ((long)(arg1)),"c" ((long)(arg2)), \ "d" ((long)(arg3)) : "memory"); \__syscall_return(type,__res); \}

inline _syscall1(int, exit, int, status);inline _syscall3(int, write, int, fd, const void*, buf, unsigned long, count);

void hello() { write(1, "Hello World\n", 12); exit(0);} な感じになる。

Page 17: Tiny hello

カーネルのシステムコールを直接叩く

これを同じようにコンパイル & リンクする。

%gcc -m32 -Os -fno-builtin -fomit-frame-pointer -fno-ident -c tiny_hello.c%ld -m elf_i386 --entry=hello -o hello tiny_hello.o %wc -c hello

strip するともう少し縮む。

%strip hello%wc -c hello

Page 18: Tiny hello

別のアプローチ : 実行形式を作る

直接 ELF オブジェクトを吐く。要するに、 ELF の形式を守ってオブジェクト内に write(1,"Hello World\n",12) を呼ぶコードを置けば良い。# http://d.hatena.ne.jp/yupo5656/20061112/p2# が詳しい。

Page 19: Tiny hello

別のアプローチ : 実行形式を作る

#include <elf.h>#include <unistd.h> // write

#define PAGE_ALIGN(adr) ((adr) & ~(0x1000 - 1)) // 16 進下 3 桁を切り捨てるだけ#define LOAD_ADDRESS PAGE_ALIGN(0x12345678) // 0x12345000 にロード#define STRING_LEN 12

#define TO_STR(s) TO_STR_(s)#define TO_STR_(s) #s

#define ECX \ TO_STR(LOAD_ADDRESS + 52 + 32) // LOAD_ADDRESS + sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr)

__asm__ ("start_: \r\n" "movl $4, %eax \r\n" // eax: system call number (__NR_write) "movl $1, %ebx \r\n" // ebx: fd (stdout) "movl $" ECX ", %ecx \r\n" // ecx: addr "movl $13, %edx \r\n" // edx: len "int $0x80 \r\n" "movl $1, %eax \r\n" // eax: system call number (__NR_exit) "movl $0, %ebx \r\n" // ebx: exit code "int $0x80 \r\n" "end_: ");extern char *start_, *end_;

Page 20: Tiny hello

別のアプローチ : 実行形式を作る

void out_elf_header() { Elf32_Ehdr ehdr = { .e_ident = { ELFMAG0, ELFMAG1, ELFMAG2 ,ELFMAG3, ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV }, .e_type = ET_EXEC, .e_machine = EM_386, .e_version = EV_CURRENT, .e_entry = LOAD_ADDRESS + sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) + STRING_LEN, .e_phoff = sizeof(Elf32_Ehdr), .e_shoff = 0, // dummy .e_flags = 0x0, .e_ehsize = sizeof(Elf32_Ehdr), .e_phentsize = sizeof(Elf32_Phdr), .e_phnum = 1, .e_shentsize = 0, // dummy .e_shnum = 0, .e_shstrndx = 0, // dummy };

write(1, &ehdr, sizeof(Elf32_Ehdr));}

Page 21: Tiny hello

別のアプローチ : 実行形式を作る

void out_program_header() { uintptr_t code_len = (uintptr_t)&end_ - (uintptr_t)&start_; Elf32_Phdr phdr = { .p_type = PT_LOAD, .p_offset = 0x0, .p_vaddr = LOAD_ADDRESS, .p_paddr = 0, // dummy .p_filesz = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) + STRING_LEN + code_len, .p_memsz = sizeof(Elf32_Ehdr) + sizeof(Elf32_Phdr) + STRING_LEN + code_len, .p_flags = PF_R | PF_X, .p_align = 0x1000, };

write(1, &phdr, sizeof(Elf32_Phdr));}

void out_code() { uintptr_t code_len = (uintptr_t)&end_ - (uintptr_t)&start_; write(1, &start_, code_len);}

int main() { out_elf_header(); out_program_header(); write(1, "hello world\n", 12); out_code(); return 0;}

Page 22: Tiny hello

別のアプローチ : 実行形式を作る

これで、以下のようにしてみよう。

%gcc tiny_hello2.c%a.out > tiny_hello2%chmod +x tiny_hello2%./tiny_hello2

動いた !!

Page 23: Tiny hello

目指せバイナリアン !!

世の中は、バイナリアンと呼ばれるマシンコードやオブジェクトコードに慣れ親しんでいる人達が居る。先述のサイトもバイナリアンな方のとこだが、ここで、 "Code Golf" なる遊びを見付けた。

http://shinh.skr.jp/dat_dir/golf_prosym.pdf

これは処理系の癖を理解して、如何に少ないコードで目的の動作を行うかというゲームっぽい。

Page 24: Tiny hello

ELF   Golf ?

他に、 "ELF Golf" というものもあるらしい。

http://shinh.skr.jp/binary/fsij061115/index.cgi?i=0

組込みプログラミングを行うには、プロセッサ、デバイス、 OS 、ブートローダ等を完全に理解し、その上でコードを書かなければならない。組込みのエキスパートを目指すならば、是非、バイナリアンになりましょう !!

Page 25: Tiny hello

バイナリアン度チェック

http://0xcc.net/binhacks/quiz.html

にある。ちなみに、私がやってみたところ、

あなたのバイナリアン度は 66 点です。すばらしいバイナリ度をお持ちのあなたは Binary Hacks にきっと夢中になるはず。役に立たなそうなハックも、ちょっとしたはずみで役に立つハックに変身することも。深追い運も急上昇中かも !?

だった。割としょっく…。

Page 26: Tiny hello

Beating the averages

http://practical-scheme.net/trans/beating-the-averages-j.html

Page 27: Tiny hello

Beating the averages

1995 年当時、私達は、多分競争相手は理解してないであろうあることを知っていた。いや、今でも理解している人はほとんどいないかもしれない。もしあなたが、あなたのサーバー上でだけ走るソフトウェアを書くのなら、あなたは自分の好むどんな言語でも使えるということだ。デスクトップソフトを書いている時にはこうはいかない。そのデスクトップのオペレーティングシステムがサポートする言語への強いバイアスがある。 10 年前なら、アプリケーションを書くということは C で書くということだった。でも Web ベースのソフトウェアなら、そして特に言語と OS のソースコードを持っているなら、あなたは好きな言語を使うことができる。

(snip)

Page 28: Tiny hello

Beating the averages実験の結果はどうだったかって ? 実際驚くことに、うまく行ったんだ。結果的に私達には 20 から 30 にものぼる競争相手が現われたが、どれひとつとして私達のソフトウェアを打ち負かすことはできなかった。私達の WYSIWYG のオンラインストアビルダーはサーバー側で走っているのに、ユーザーからはまるでデスクトップアプリケーションを使っているかのように感じられた。競争相手は CGI スクリプトを使っていた。そして機能的にも、私達は相手のはるか先を行っていた。しばしば、競争相手は追い詰められて、私達のソフトに無い機能を入れようと試みた。しかし、 Lisp のおかげで私達の開発サイクルは非常に速く、相手がプレスリリースを出した 1日 2日後にもう同様の機能を作ったこともしばしばある。プレスリリースをカバーした記者が私達のところに取材に来る頃には、こちらにももう新しい機能が追加されていたのだ。

きっと競争相手には、私達が何か秘密兵器を持っているかのように見えたに違いない。彼等の暗号通信を解読しているとか、そんなことだ。事実、私達は秘密兵器を持っていたんだが、それは彼等が思っているよりずっと簡単なことだった。誰も彼等の計画を私達に漏らしたりしなかった。単に、私達が他の誰よりも素早くソフトウェアを開発できたというだけのことだ。

(snip)

Page 29: Tiny hello

Beating the averages私達の秘密兵器も同じようなものだ。私達はソフトウェアを、括弧だらけの奇怪な構文を持つ、妙ちきりんな人工知能言語で書いた。正直なところ何年もの間、私は Lisp がそんなふうに呼ばれることが気にさわっていたのだが、今やそれは私達のアドバンテージとなったのだ。ビジネスでは、競争相手が理解出来ない技術的アドバンテージを持っていることほど価値あることは無い。ビジネスでは、驚きは軍隊ほどに価値がある。

そして、ちょっと恥をしのんで告白すると、私達が Viaweb を作っている間、私は Lisp について一切公言しなかった。プレス発表でも言わなかったし、私達の Web サイトで "Lisp" を検索しても見つかるのは私の書いた Lisp に関する 2冊の書物のタイトルだけだ。これは偶然そうなったわけじゃない。ベンチャー企業はライバルになるべく情報を漏らさないものだ。もしライバルが、私達のソフトが何で書かれているか知らないのなら、あるいは気にしないのなら、私はそれをそのままにしておきたかった [注 2] 。

Page 30: Tiny hello

Beating the averages私達の技術を一番理解していたのは私達の顧客だった。お客さんは Viaweb が何の言語で書かれているかなんて気にしないが、ソフトがとにかく使えるということに気付いてくれた。私達のソフトを使えば、見栄えがいいオンラインストアを文字通り数分のうちに作ることができた。そしてほとんど口コミによって私達は顧客を獲得していった。 1996 年の終りまでに、 70店程がオンラインストアを実現した。 1997 年の終りには 500店になった。その 6ヵ月後、 Yahoo が私達の会社を買い取った時、顧客数は 1070店になっていた。こんにち、私達のソフトウェアは Yahoo Store としてこの領域のマーケットを独占している。それは Yahoo の中で最も利益を上げている部分の一つであり、それによって作られたオンラインストアが Yahoo Shopping の基礎となっている。私は 1999 年に Yahoo を離れたので、現在どのくらいのユーザーがいるのかは知らないが、最後 聞いた時はだいたノい 14000 ということだった。

時々、 Yahoo Store は今でも Lisp を使っているのかと聞かれる。答えはイエスだ。Lisp コードはそっくりそのまま、まだある。 Yahoo はサーバー側のソフトウェアを、エリック・レイモンドが薦めた 5 つの言語全てを使って書いている。

(snip)

Page 31: Tiny hello

Beating the averages私達の技術を一番理解していたのは私達の顧客だった。お客さんは Viaweb が何の言語で書かれているかなんて気にしないが、ソフトがとにかく使えるということに気付いてくれた。私達のソフトを使えば、見栄えがいいオンラインストアを文字通り数分のうちに作ることができた。そしてほとんど口コミによって私達は顧客を獲得していった。 1996 年の終りまでに、 70店程がオンラインストアを実現した。 1997 年の終りには 500店になった。その 6ヵ月後、 Yahoo が私達の会社を買い取った時、顧客数は 1070店になっていた。こんにち、私達のソフトウェアは Yahoo Store としてこの領域のマーケットを独占している。それは Yahoo の中で最も利益を上げている部分の一つであり、それによって作られたオンラインストアが Yahoo Shopping の基礎となっている。私は 1999 年に Yahoo を離れたので、現在どのくらいのユーザーがいるのかは知らないが、最後 聞いた時はだいたノい 14000 ということだった。

時々、 Yahoo Store は今でも Lisp を使っているのかと聞かれる。答えはイエスだ。Lisp コードはそっくりそのまま、まだある。 Yahoo はサーバー側のソフトウェアを、エリック・レイモンドが薦めた 5 つの言語全てを使って書いている。

(snip)

Page 32: Tiny hello

Beating the averagesもしあなたがベンチャーで働いているなら、競争相手の実力を見積もる便利なヒントをあげよう。人材募集内容を見るんだ。彼等のウェブには他に見栄えのいい写真や美辞麗句で彩られているだろうけど、募集内容だけは彼等が一番欲しているものを的確に表現しているはずだ。でなければ見当違いの人しか応募して来ないからね。

Viaweb で働いていた期間、私は人材募集記事をたくさん見た。新しい競争相手は毎月のように現われた。いつも最初に私がしたことは、まずオンラインデモがあるかどうかをチェックして、それから人材募集を見ることだった。 1~ 2 年したら、警戒すべきライバルとそうでないところとを見分けられるようになった。人材募集がいかにも IT といった匂いをただよわせていればいるほど、その企業は脅威ではない。一番安全なのは Oracle の経験者を募集しているところだ。そういうところを警戒する必要は全く無い。また、 Javaや C++ プログラマを募集しているところも安全だ。もし Perl や Python プログラマを募集していたら、ちょっと気を付けたほうがいい。その企業の、少なくとも技術部門は本物のハッカーがやっている可能性が高いからだ。もし私が Lisp ハッカーの募集広告を目にしていたら、きっとかなり心配していただろう。

Page 33: Tiny hello

Beating the averagesLisp の本を書いていた時、私は皆が Lisp を分かってくれたらいいと願っていたものだ。 Viaweb を立ち上げた時、私の見方は変わった。 Lisp を分かって欲しい。但し競争相手以外に、だ。

SICP を読みましょう。

Page 34: Tiny hello

顧客が本当に必要だったもの

Page 35: Tiny hello

顧客が本当に必要だったもの

Page 36: Tiny hello

おわり