glibcを使わないでHello Worldを書く。FreeBSD版その1

対象

この記事で取り扱っているのは、Pentium III, FreeBSD 6.1-RELEASEです。

概要

Binary Hacks ―ハッカー秘伝のテクニック100選

Binary Hacks ―ハッカー秘伝のテクニック100選

この書籍では、Linuxglibcを使わず、直接システムコールを呼び出すことで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
(略)

次のファイルをコピーします。

  1. /usr/src/lib/libc/_exit.S
  2. /usr/src/lib/libc/write.S
  3. /usr/src/lib/libc/i386/SYS.h
  4. /usr/src/lib/libc/i386/sys/cerror.S/
  5. /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   80484ac 
 8048370:	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 "があるので、/usr/include/machine/asm.hをのぞいてみます。

(略)
#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 "もあります。こちらものぞいてみます。ファイルは、/usr/include/sys/syscall.hになります。

/*
 * 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)

ここまでで、コピーするファイルが出揃いました。以下にまとめます。

  1. /usr/src/lib/libc/_exit.S
  2. /usr/src/lib/libc/write.S
  3. /usr/src/lib/libc/i386/SYS.h
  4. /usr/src/lib/libc/i386/sys/cerror.S/
  5. /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 WorldFreeBSD版ができました。実行してみます。

$ ./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にアップしました。興味のある方は、ご参照ください。