glibcを使わないでHello Worldを書く。FreeBSD版その1
対象
この記事で取り扱っているのは、Pentium III, FreeBSD 6.1-RELEASEです。
概要
Binary Hacks ―ハッカー秘伝のテクニック100選
- 作者: 高林哲,鵜飼文敏,佐藤祐介,浜地慎一郎,首藤一幸
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2006/11/14
- メディア: 単行本(ソフトカバー)
- 購入: 23人 クリック: 383回
- この商品を含むブログ (223件) を見る
この書籍では、Linuxでglibcを使わず、直接システムコールを呼び出すことでHello Worldを書き、バイナリを小さくしています。ここでは、それをFreeBSDを使って実現します(FreeBSDなので、使わないのはglibcではなく、libcになります)。
実現方法
以下の方法で、4734バイトあったバイナリを、2112バイトまで小さくしました。
まず、/usr/src/lib/libcでmake dependします。
$ sudo make depend [/usr/src/lib/libc] yacc -d -p_nsyy /usr/src/lib/libc/net/nsparser.y mv y.tab.c nsparser.c mv y.tab.h nsparser.h lex -P_nsyy -o/dev/stdout /usr/src/lib/libc/net/nslexer.l | sed -e '/YY_BUF_SI ZE/s/16384/1024/' >nslexer.c printf '#include "SYS.h"\nRSYSCALL(fork)\n' > fork.S printf '#include "SYS.h"\nRSYSCALL(read)\n' > read.S printf '#include "SYS.h"\nRSYSCALL(write)\n' > write.S (略)
次のファイルをコピーします。
- /usr/src/lib/libc/_exit.S
- /usr/src/lib/libc/write.S
- /usr/src/lib/libc/i386/SYS.h
- /usr/src/lib/libc/i386/sys/cerror.S/
- /usr/src/lib/libc/sys/__error.c
次のようなhello.cを書きます。
#include <sys/types.h> int errno; ssize_t write(int fd, void* src, size_t len); void _exit(int status); void hello() { write(1, "Hello, world!\n", 14); _exit(0); }
これらのファイルをコンパイルします。
$ gcc -o __error.o -Wall -c __error.c $ gcc -o _exit.o -Wall -c _exit.S $ gcc -o cerror.o -Wall -c cerror.S $ gcc -o hello.o -Wall -c hello.c $ gcc -o write.o -Wall -c write.S
エントリポイントをhello.cのhello関数になるようにしてリンクします。
$ ld --entry=hello -o hello hello.o write.o _exit.o cerror.o __error.o
これでlibcを使わないHello Worldができました。実行して動くかどうか確認します。
$ ./hello [~/projects/hello] Hello, world!
ちゃんと動きます。lddで依存関係を調べてみます。
$ ldd hello [~/projects/hello] ldd: hello: not a dynamic executable
大きさは2112バイトになりました。
$ wc -c hello [~/projects/hello] 2112 hello
詳細
「BINARY HACKS」では、マクロをコピーして、システムコールを直接呼び出す関数を記述する、という方法がとられていました。今回もそれにならい、システムコールを呼び出している箇所をコピーして使うことにします。
その前に、通常のHello Worldはどれくらいの大きさになるのでしょうか? これを確かめるため、以下のhello.cを書きます。
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); return 0; }
次のMakefileを書いて、コンパイルします。
PKG = hello OBJS = hello.o #OPT = -g OPT = all: $(PKG) $(PKG): $(OBJS) gcc -o $@ $(OBJS) .c.o: gcc -o $@ -Wall $(OPT) -c $< clean: rm -f $(PKG) $(OBJS) .PHONY: clean all
$ make [~/projects/standard_hello] gcc -o hello.o -Wall -c hello.c gcc -o hello hello.o
大きさは、
$ wc -c hello [~/projects/standard_hello] 4734 hello
になりました。このhelloは、libcに依存しています。
$ ldd hello [~/projects/standard_hello] hello: libc.so.6 => /lib/libc.so.6 (0x28074000)
helloの内部をnmとobjdumpでみてみます。
$ nm hello [~/projects/standard_hello] 08049610 A _DYNAMIC 080496bc A _GLOBAL_OFFSET_TABLE_ w _Jv_RegisterClasses 080496ac d __CTOR_END__ 080496a8 d __CTOR_LIST__ 080496b4 d __DTOR_END__ 080496b0 d __DTOR_LIST__ 0804960c r __EH_FRAME_BEGIN__ 0804960c r __FRAME_END__ 080496b8 d __JCR_END__ 080496b8 d __JCR_LIST__ 080496d8 A __bss_start w __deregister_frame_info 0804852c t __do_global_ctors_aux 08048460 t __do_global_dtors_aux 08049604 D __dso_handle 08049600 D __progname w __register_frame_info 080496d8 A _edata 080496f8 A _end 08048550 T _fini 08048368 T _init U _init_tls 080483cc T _start U atexit 080496d8 b completed.1 080496f4 B environ U exit 080484ac t frame_dummy 080484f8 T main 080496dc b object.2 08049608 d p.0 U printf
$ objdump -d hello [~/projects/hello] hello: file format elf32-i386-freebsd Disassembly of section .init: 08048368 <_init>: 8048368: 83 ec 0c sub $0xc,%esp 804836b: e8 3c 01 00 00 call 80484ac8048370: e8 b7 01 00 00 call 804852c <__do_global_ctors_aux> 8048375: 83 c4 0c add $0xc,%esp 8048378: c3 ret Disassembly of section .plt: 0804837c <.plt>: 804837c: ff 35 c0 96 04 08 pushl 0x80496c0 8048382: ff 25 c4 96 04 08 jmp *0x80496c4 8048388: 00 00 add %al,(%eax) 804838a: 00 00 add %al,(%eax) 804838c: ff 25 c8 96 04 08 jmp *0x80496c8 8048392: 68 00 00 00 00 push $0x0 8048397: e9 e0 ff ff ff jmp 804837c <_init+0x14> 804839c: ff 25 cc 96 04 08 jmp *0x80496cc 80483a2: 68 08 00 00 00 push $0x8 80483a7: e9 d0 ff ff ff jmp 804837c <_init+0x14> 80483ac: ff 25 d0 96 04 08 jmp *0x80496d0 80483b2: 68 10 00 00 00 push $0x10 80483b7: e9 c0 ff ff ff jmp 804837c <_init+0x14> 80483bc: ff 25 d4 96 04 08 jmp *0x80496d4 80483c2: 68 18 00 00 00 push $0x18 80483c7: e9 b0 ff ff ff jmp 804837c <_init+0x14> Disassembly of section .text: 080483cc <_start>: 80483cc: 55 push %ebp 80483cd: 89 e5 mov %esp,%ebp 80483cf: 57 push %edi 80483d0: 56 push %esi 80483d1: 53 push %ebx 80483d2: 83 ec 0c sub $0xc,%esp 80483d5: 83 e4 f0 and $0xfffffff0,%esp 80483d8: 8b 5d 04 mov 0x4(%ebp),%ebx 80483db: 89 d7 mov %edx,%edi 80483dd: 8d 74 9d 0c lea 0xc(%ebp,%ebx,4),%esi 80483e1: 85 db test %ebx,%ebx 80483e3: 89 35 f4 96 04 08 mov %esi,0x80496f4 80483e9: 7e 24 jle 804840f <_start+0x43> 80483eb: 8b 45 08 mov 0x8(%ebp),%eax 80483ee: 85 c0 test %eax,%eax 80483f0: 74 1d je 804840f <_start+0x43> 80483f2: a3 00 96 04 08 mov %eax,0x8049600 80483f7: 89 c1 mov %eax,%ecx 80483f9: 8a 01 mov (%ecx),%al 80483fb: 84 c0 test %al,%al 80483fd: 74 10 je 804840f <_start+0x43> 80483ff: 90 nop 8048400: 3c 2f cmp $0x2f,%al 8048402: 8d 51 01 lea 0x1(%ecx),%edx 8048405: 74 45 je 804844c <_start+0x80> 8048407: 89 d1 mov %edx,%ecx 8048409: 8a 01 mov (%ecx),%al 804840b: 84 c0 test %al,%al 804840d: 75 f1 jne 8048400 <_start+0x34> 804840f: b8 10 96 04 08 mov $0x8049610,%eax 8048414: 85 c0 test %eax,%eax 8048416: 74 3e je 8048456 <_start+0x8a> 8048418: 83 ec 0c sub $0xc,%esp 804841b: 57 push %edi 804841c: e8 8b ff ff ff call 80483ac <_init+0x44> 8048421: 83 c4 10 add $0x10,%esp 8048424: 83 ec 0c sub $0xc,%esp 8048427: 68 50 85 04 08 push $0x8048550 804842c: e8 7b ff ff ff call 80483ac <_init+0x44> 8048431: e8 32 ff ff ff call 8048368 <_init> 8048436: 50 push %eax 8048437: 56 push %esi 8048438: 8d 45 08 lea 0x8(%ebp),%eax 804843b: 50 push %eax 804843c: 53 push %ebx 804843d: e8 b6 00 00 00 call 80484f8 8048442: 83 c4 14 add $0x14,%esp 8048445: 50 push %eax 8048446: e8 71 ff ff ff call 80483bc <_init+0x54> 804844b: 90 nop 804844c: 89 d1 mov %edx,%ecx 804844e: 89 15 00 96 04 08 mov %edx,0x8049600 8048454: eb b3 jmp 8048409 <_start+0x3d> 8048456: e8 41 ff ff ff call 804839c <_init+0x34> 804845b: eb c7 jmp 8048424 <_start+0x58> 804845d: 90 nop 804845e: 90 nop 804845f: 90 nop 08048460 <__do_global_dtors_aux>: 8048460: 55 push %ebp 8048461: 89 e5 mov %esp,%ebp 8048463: 83 ec 08 sub $0x8,%esp 8048466: 80 3d d8 96 04 08 00 cmpb $0x0,0x80496d8 804846d: 74 0f je 804847e <__do_global_dtors_aux+0x1e> 804846f: eb 38 jmp 80484a9 <__do_global_dtors_aux+0x49> 8048471: 8d 76 00 lea 0x0(%esi),%esi 8048474: 83 c0 04 add $0x4,%eax 8048477: a3 08 96 04 08 mov %eax,0x8049608 804847c: ff d2 call *%edx 804847e: a1 08 96 04 08 mov 0x8049608,%eax 8048483: 8b 10 mov (%eax),%edx 8048485: 85 d2 test %edx,%edx 8048487: 75 eb jne 8048474 <__do_global_dtors_aux+0x14> 8048489: b8 00 00 00 00 mov $0x0,%eax 804848e: 85 c0 test %eax,%eax 8048490: 74 10 je 80484a2 <__do_global_dtors_aux+0x42> 8048492: 83 ec 0c sub $0xc,%esp 8048495: 68 0c 96 04 08 push $0x804960c 804849a: e8 61 7b fb f7 call 0 <_init-0x8048368> 804849f: 83 c4 10 add $0x10,%esp 80484a2: c6 05 d8 96 04 08 01 movb $0x1,0x80496d8 80484a9: c9 leave 80484aa: c3 ret 80484ab: 90 nop 080484ac : 80484ac: 55 push %ebp 80484ad: b8 00 00 00 00 mov $0x0,%eax 80484b2: 89 e5 mov %esp,%ebp 80484b4: 83 ec 08 sub $0x8,%esp 80484b7: 85 c0 test %eax,%eax 80484b9: 74 15 je 80484d0 80484bb: 83 ec 08 sub $0x8,%esp 80484be: 68 dc 96 04 08 push $0x80496dc 80484c3: 68 0c 96 04 08 push $0x804960c 80484c8: e8 33 7b fb f7 call 0 <_init-0x8048368> 80484cd: 83 c4 10 add $0x10,%esp 80484d0: a1 b8 96 04 08 mov 0x80496b8,%eax 80484d5: 85 c0 test %eax,%eax 80484d7: 74 1b je 80484f4 80484d9: b8 00 00 00 00 mov $0x0,%eax 80484de: 85 c0 test %eax,%eax 80484e0: 74 12 je 80484f4 80484e2: 83 ec 0c sub $0xc,%esp 80484e5: 68 b8 96 04 08 push $0x80496b8 80484ea: e8 11 7b fb f7 call 0 <_init-0x8048368> 80484ef: 83 c4 10 add $0x10,%esp 80484f2: 89 f6 mov %esi,%esi 80484f4: c9 leave 80484f5: c3 ret 80484f6: 90 nop 80484f7: 90 nop 080484f8 : 80484f8: 55 push %ebp 80484f9: 89 e5 mov %esp,%ebp 80484fb: 83 ec 08 sub $0x8,%esp 80484fe: 83 e4 f0 and $0xfffffff0,%esp 8048501: b8 00 00 00 00 mov $0x0,%eax 8048506: 83 c0 0f add $0xf,%eax 8048509: 83 c0 0f add $0xf,%eax 804850c: c1 e8 04 shr $0x4,%eax 804850f: c1 e0 04 shl $0x4,%eax 8048512: 29 c4 sub %eax,%esp 8048514: 83 ec 0c sub $0xc,%esp 8048517: 68 a7 85 04 08 push $0x80485a7 804851c: e8 6b fe ff ff call 804838c <_init+0x24> 8048521: 83 c4 10 add $0x10,%esp 8048524: b8 00 00 00 00 mov $0x0,%eax 8048529: c9 leave 804852a: c3 ret 804852b: 90 nop 0804852c <__do_global_ctors_aux>: 804852c: 55 push %ebp 804852d: 89 e5 mov %esp,%ebp 804852f: 53 push %ebx 8048530: 52 push %edx 8048531: bb a8 96 04 08 mov $0x80496a8,%ebx 8048536: a1 a8 96 04 08 mov 0x80496a8,%eax 804853b: eb 0a jmp 8048547 <__do_global_ctors_aux+0x1b> 804853d: 8d 76 00 lea 0x0(%esi),%esi 8048540: 83 eb 04 sub $0x4,%ebx 8048543: ff d0 call *%eax 8048545: 8b 03 mov (%ebx),%eax 8048547: 83 f8 ff cmp $0xffffffff,%eax 804854a: 75 f4 jne 8048540 <__do_global_ctors_aux+0x14> 804854c: 58 pop %eax 804854d: 5b pop %ebx 804854e: c9 leave 804854f: c3 ret Disassembly of section .fini: 08048550 <_fini>: 8048550: 83 ec 0c sub $0xc,%esp 8048553: e8 08 ff ff ff call 8048460 <__do_global_dtors_aux> 8048558: 83 c4 0c add $0xc,%esp 804855b: c3 ret
では、コピーするために、FreeBSDでwriteシステムコールを呼び出している箇所を探します。これはしかし、例えば、
$ grep -r write * [/usr/src]
とやっても、見付けられません。というのはFreeBSDでは、システムコールを呼び出す箇所は、構築時に自動で生成されるからです(なんでこれがわかったのかについては、思い出せません)。これを生成させるためには、/usr/src/lib/libcでmake dependします。すると、/usr/src/lib/libcに、write.Sをはじめとするファイルが生成されます。write.Sは、以下のようになっています。
#include "SYS.h" RSYSCALL(write)
ここでSYS.hというのは、findして探してみると、アーキテクチャごとにあることが分かります。
$ find . -name SYS.h [/usr/src/lib/libc] ./alpha/SYS.h ./amd64/SYS.h ./arm/SYS.h ./i386/SYS.h ./ia64/SYS.h ./powerpc/SYS.h ./sparc64/SYS.h
今回使っているPCのCPUはPentiumなので、ここでは/usr/src/lib/libc/i386/SYS.hを使用します。さらに、このSYS.hの中身は、以下のようになっています。
(略) #include <sys/syscall.h> #include <machine/asm.h> #define SYSCALL(x) 2: PIC_PROLOGUE; jmp PIC_PLT(HIDENAME(cerror)); \ ENTRY(__CONCAT(__sys_,x)); \ .weak CNAME(x); \ .set CNAME(x),CNAME(__CONCAT(__sys_,x)); \ .weak CNAME(__CONCAT(_,x)); \ .set CNAME(__CONCAT(_,x)),CNAME(__CONCAT(__sys_,x)); \ mov __CONCAT($SYS_,x),%eax; KERNCALL; jb 2b #define RSYSCALL(x) SYSCALL(x); ret #define PSEUDO(x) ENTRY(__CONCAT(__sys_,x)); \ .weak CNAME(__CONCAT(_,x)); \ .set CNAME(__CONCAT(_,x)),CNAME(__CONCAT(__sys_,x)); \ mov __CONCAT($SYS_,x),%eax; KERNCALL; ret /* gas messes up offset -- although we don't currently need it, do for BCS */ #define LCALL(x,y) .byte 0x9a ; .long y; .word x #define KERNCALL int $0x80
どうやら、SYSCALLマクロ(とRSYSCALLマクロ)、PSEUDOマクロで、システムコールを呼び出す関数を定義できるようです。これらの中には、ENTRYやHIDENAMEといった知らない名前があります。これらの定義はどこにあるのでしょうか? "#include
(略) #ifdef PIC #define PIC_PROLOGUE \ pushl %ebx; \ call 1f; \ 1: \ popl %ebx; \ addl $_GLOBAL_OFFSET_TABLE_+[.-1b],%ebx #define PIC_EPILOGUE \ popl %ebx #define PIC_PLT(x) x@PLT #define PIC_GOT(x) x@GOT(%ebx) #else #define PIC_PROLOGUE #define PIC_EPILOGUE #define PIC_PLT(x) x #define PIC_GOT(x) x #endif (略) #define CNAME(csym) csym #define HIDENAME(asmsym) .asmsym /* XXX should use .p2align 4,0x90 for -m486. */ #define _START_ENTRY .text; .p2align 2,0x90 #define _ENTRY(x) _START_ENTRY; \ .globl CNAME(x); .type CNAME(x),@function; CNAME(x): (略) #define ENTRY(x) _ENTRY(x) (略)
どうやら、/usr/include/machine/asm.hがあればいいようです。それともうひとつ、SYS.hに"#include
/* * System call numbers. * * DO NOT EDIT-- this file is automatically generated. * $FreeBSD: src/sys/sys/syscall.h,v 1.178.2.2 2006/03/17 01:47:33 rwatson Exp $ * created from FreeBSD: src/sys/kern/syscalls.master,v 1.198.2.2 2006/03/17 01: 47:06 rwatson Exp */ #define SYS_syscall 0 #define SYS_exit 1 #define SYS_fork 2 #define SYS_read 3 #define SYS_write 4 (略)
ここではシステムコールの番号を定義しているようです。これをふまえてSYS.hのSYSCALLマクロをみると、例えばSYSCALL(write)とした場合、最後の行の、
mov __CONCAT($SYS_,x),%eax
が、
mov $SYS_write,%eax
になり、eaxレジスタにシステムコールの番号(この場合は4)を入力しているように読めます。それと、SYS.hのSYSCALLマクロをみると、最初の行に、
jmp PIC_PLT(HIDENAME(cerror))
というのがあります。cerrorというところにジャンプするようです。cerrorとはどのようなものか、探してみます。
$ find . -name 'cerror*' [/usr/src/lib/libc] ./alpha/sys/cerror.S ./amd64/sys/cerror.S ./arm/sys/cerror.S ./i386/sys/cerror.S ./ia64/sys/cerror.S ./powerpc/sys/cerror.S ./sparc64/sys/cerror.S
これもアーキテクチャごとにあります。すれば、今回使用するのは/usr/src/lib/libc/i386/sys/cerror.Sになります。これの中身は、以下のようになっています。
(略) .globl HIDENAME(cerror) /* * The __error() function is thread aware. For non-threaded * programs and the initial threaded in threaded programs, * it returns a pointer to the global errno variable. */ .globl CNAME(__error) .type CNAME(__error),@function HIDENAME(cerror): pushl %eax #ifdef PIC /* The caller must execute the PIC prologue before jumping to cerror. */ call PIC_PLT(CNAME(__error)) popl %ecx PIC_EPILOGUE #else call CNAME(__error) popl %ecx #endif movl %ecx,(%eax) movl $-1,%eax movl $-1,%edx ret
ここでcerrorの実体を定義しているようですので、このファイルもwrite.Sには必要なようです。またこのcerror.Sは__errorという関数をcallしています。これも探します。
$ find . -name '__error*' [/usr/src/lib/libc] ./sys/__error.c
/usr/src/lib/libc/sys/__error.cは、以下のようになっています。
(略) extern int errno; /* * Declare a weak reference in case the application is not linked * with libpthread. */ __weak_reference(__error_unthreaded, __error); int * __error_unthreaded() { return(&errno); }
これも必要になるので、コピーします。write.Sには、以上のファイルがあればいいようです。
さて、Hello Worldを作るのに必要なのはwriteシステムコールだけではなく、exitシステムコールも必要です。ではexit.Sもコピーする、といきたいところですが、exit.Sというのはありません。探してみたところ、
$ find . -name '*exit*' [/usr/src/lib/libc] ./stdlib/atexit.3 ./stdlib/atexit.c ./stdlib/atexit.h ./stdlib/exit.3 ./stdlib/exit.c ./sys/_exit.2 ./_exit.S ./kse_exit.S ./thr_exit.S
がありました。/usr/src/lib/libc/_exit.Sは、以下のようになっています。このファイルが探していたファイルのようです。
#include "SYS.h" PSEUDO(exit)
ここまでで、コピーするファイルが出揃いました。以下にまとめます。
- /usr/src/lib/libc/_exit.S
- /usr/src/lib/libc/write.S
- /usr/src/lib/libc/i386/SYS.h
- /usr/src/lib/libc/i386/sys/cerror.S/
- /usr/src/lib/libc/sys/__error.c
次に、本体のhello.cを書きます。以下のようにします。
#include <sys/types.h> int errno; ssize_t write(int fd, void* src, size_t len); void _exit(int status); void hello() { write(1, "Hello, world!\n", 14); _exit(0); }
ここで、
int errno;
というのは、__error.cで、以下のように参照されている変数をつくるためです。
extern int errno;
hello.cにあるのは、あとは呼び出す関数のためのプロトタイプ宣言と、Hello World本体です。
次のようなMakefileを書いて、これらのファイルをコンパイル、リンクします。
PKG = hello OBJS = hello.o write.o _exit.o cerror.o __error.o #OPT = -g -v OPT = all: $(PKG) $(PKG): $(OBJS) ld --entry=hello -o $@ $(OBJS) hello.o: hello.c gcc -o $@ -Wall $(OPT) -c $< write.o: write.S SYS.h gcc -o $@ -Wall $(OPT) -c write.S _exit.o: _exit.S SYS.h gcc -o $@ -Wall $(OPT) -c _exit.S cerror.o: cerror.S SYS.h gcc -o $@ -Wall $(OPT) -c cerror.S __error.o: __error.c gcc -o $@ -Wall $(OPT) -c $< clean: rm -f $(PKG) $(OBJS) .PHONY: clean all
「BINARY HACKS」にも書いてありますが、ldが使用するデフォルトのエントリポイントは_startで、今回は存在しません。そこで、エントリポイントをhello.cのhello関数になるようにしてリンクしています。
$ make [~/projects/hello] gcc -o hello.o -Wall -c hello.c gcc -o write.o -Wall -c write.S gcc -o _exit.o -Wall -c _exit.S gcc -o cerror.o -Wall -c cerror.S gcc -o __error.o -Wall -c __error.c ld --entry=hello -o hello hello.o write.o _exit.o cerror.o __error.o
以上で、libcを使わないHello WorldのFreeBSD版ができました。実行してみます。
$ ./hello [~/projects/hello] Hello, world!
ちゃんと動作します。大きさは2112バイトで、標準的なHello Worldの半分程度になりました。
$ wc -c hello [~/projects/hello] 2112 hello
lddコマンドで調べると、依存関係はありません。
$ ldd hello [~/projects/hello] ldd: hello: not a dynamic executable
nmコマンドでシンボルテーブルを調べると、標準的なHello Worldよりシンボルが少なくなりました。
$ nm hello [~/projects/hello] 080480b4 T .cerror 080490e4 A __bss_start 080480c8 W __error 080480c8 T __error_unthreaded 080480ac T __sys_exit 080480a0 T __sys_write 080490e4 A _edata 080490e8 A _end 080480ac W _exit 080480a0 W _write 080490e4 B errno 08048074 T hello 080480a0 W write
objdumpしても、短いです。
$ objdump -d hello [~/projects/hello] hello: file format elf32-i386-freebsd Disassembly of section .text: 08048074: 8048074: 55 push %ebp 8048075: 89 e5 mov %esp,%ebp 8048077: 83 ec 08 sub $0x8,%esp 804807a: 83 ec 04 sub $0x4,%esp 804807d: 6a 0e push $0xe 804807f: 68 d2 80 04 08 push $0x80480d2 8048084: 6a 01 push $0x1 8048086: e8 15 00 00 00 call 80480a0 <__sys_write> 804808b: 83 c4 10 add $0x10,%esp 804808e: 83 ec 0c sub $0xc,%esp 8048091: 6a 00 push $0x0 8048093: e8 14 00 00 00 call 80480ac <__sys_exit> 8048098: e9 17 00 00 00 jmp 80480b4 <.cerror> 804809d: 8d 76 00 lea 0x0(%esi),%esi 080480a0 <__sys_write>: 80480a0: b8 04 00 00 00 mov $0x4,%eax 80480a5: cd 80 int $0x80 80480a7: 72 ef jb 8048098 80480a9: c3 ret 80480aa: 90 nop 80480ab: 90 nop 080480ac <__sys_exit>: 80480ac: b8 01 00 00 00 mov $0x1,%eax 80480b1: cd 80 int $0x80 80480b3: c3 ret 080480b4 <.cerror>: 80480b4: 50 push %eax 80480b5: e8 0e 00 00 00 call 80480c8 <__error_unthreaded> 80480ba: 59 pop %ecx 80480bb: 89 08 mov %ecx,(%eax) 80480bd: b8 ff ff ff ff mov $0xffffffff,%eax 80480c2: ba ff ff ff ff mov $0xffffffff,%edx 80480c7: c3 ret 080480c8 <__error_unthreaded>: 80480c8: 55 push %ebp 80480c9: 89 e5 mov %esp,%ebp 80480cb: b8 e4 90 04 08 mov $0x80490e4,%eax 80480d0: c9 leave 80480d1: c3 ret
以上が、FreeBSDでlibcを使わずにHello Worldを書く方法です。実際には、libcからソースコードをコピーしているので、使っていないという言い方は語弊があるかもしれませんが、リンクはしなくなりました。今回、SYS.hはそのまま使用しましたが、いらないところも含まれていると思いますから、それらを削除すれば、さらに小さくなると思います。また、「BINARY HACKS」では、コンパイラにサイズを小さくするオプションをつけたり、stripしたりしているので、同じことを試みようと思います。
今回作成したファイル一式をhttp://nekomimists.ddo.jp/~tom/repository/minimum_hello-1.0.tar.gzにアップしました。興味のある方は、ご参照ください。