昨日の続き

環境は FreeBSD 6.1 (AMD64) で、試したのは libc なしの Hello, world.
そういえば 0 をセットするなら xor を使うと小さくなるなんて話があった事を思い出したのでやってみる。1の作り方はxorで0にしてincすると1byte小さくなるようだ。もっと賢い方法があるかもしれないが。

#include <sys/syscall.h>
        .section .rodata
.HELLO:
        .ascii "Hello, world.\n"
.globl _start
        .section .init
_start:
        movq $SYS_write, %rax
        xorq %rdi,%rdi
        inc %rdi
        movq $.HELLO,%rsi
        movq $14,%rdx
        syscall
        xorq %rdi,%rdi
        xorq %rax, %rax
        inc %rax
        syscall                

システムコール番号は _exit が 1 で、 write は 4。
これを strip (.bss, .data, .text も削る)、すると 512 byte となる。昨日は .text を削り忘れていた。
LinuxFreeBSD の違いはあるが、本の例で488byte。64bit 用だとアドレスが入る所1か所で4byte大きくなり、ELFヘッダで 12 byte、プログラムヘッダの1エントリで12byte(これが2つ分)、セクションヘッダ1つで8byte(これ4つ)大きくなる計算だ。まあ妥当な所か。こっちはアセンブラで書いてる分得している所もあるだろう。

ちょっとずるい気もするが、xorq, movq, でなく xorl, movl を使い、レジスタも eax, edi, esi, edx を使うとさらに縮む。さらに %eax に mov する所を %el にするとまた縮む。これはやって良い事なのか?ともあれプログラムがexecve された時にレジスタが0クリアされるならば動くだろう。確認しろって?

という事で、execve(2) の実装を調べた。これは、引数をコピーインして kern_execve を呼ぶ。結局たどり着くのは do_execve (sys/kern/kern_exec.c)、exec_setregs で起動されるプログラムに渡す値がセットされる。この関数は当然アーキテクチャ依存で、AMD64の場合 sys/amd64/amd64/machdep.c にある。ここでスレッドのレジスタを格納する構造体を一旦 bzero でクリアしているので、多くの汎用レジスタは 0 になる。勿論 rip はエントリポイントになるし、 rsp はスタックポインタだ。あと、rdi は引数 argv を指す。

という事で、プログラムの各セクションが0xffffffff 以下にロードされていたからrdi, rsi の下位32bitだけ、また0クリアのおかげで rax は下位8bitセットするだけで動いた、という訳だったようだ。

命令プレフィックスが付くので xor でクリアするなら下手に16bitにするより32bit命令の方が短くなる、とか面白いね。即値はたぶん小さいビット数の方が得をするかな?がんばるなら命令セットマニュアルは必携だろう。もうがんばらない予定。

一応今までがんばった結果はこれ。504byteだった。

        .section .rodata
.L
        .ascii "Hello, world.\n"
.globl _start
        .section .init
_start:
        mov $4,%al
        xor %edi,%edi
        inc %edi
        mov $1,%dl
        mov $.L,%esi
        mov $14,%edx
        syscall
        xor %edi,%edi
        mov $1,%al
        syscall

流行に乗り遅れたが

筆者の周囲でも流行した「Binary Hack」にインスパイヤされてみた。
環境は FreeBSD 6.1 (AMD64) で、試したのは libc なしの Hello, world.

#include <sys/syscall.h>
        .section .rodata
.HELLO:
        .ascii "Hello, world.\n"
.globl _start
        .section .init
_start:
        movq $SYS_write, %rax
        movq $1, %rdi
        movq $.HELLO,%rsi
        movq $14,%rdx
        syscall
        movq $SYS_exit, %rax
        syscall                

システムコールの呼び方は /usr/src/lib/libc/amd64/SYS.h を見る。AMD64 の場合は syscall 命令だ。システムコール番号と引数をレジスタに入れて叩く。システムコール番号自体は sys/syscall.h に書かれているので include した。

このファイル hello.S を gcc -nostdlib hello.S でアセンブルすると、a.out のサイズが 1460byte, strip して 736byte だ。さらに .data と .bss を strip して、できたファイルは592byteとなった。あと .shstrtab なるセクションが残っているが、これは除去できず。検索した所、セクション名文字列のテーブルらしい。

追記: かなり既出ネタ。

はてな内でも、 id:SumiTomohiko:20061221 により既出だった。

ここは何か?

簡潔に

はてな」ってどうよ?という自らの疑問に答える為のもの。

事の起こり

はてな」は Web2.0 的とか日本の Google 的存在とか持ち上げられている一方、筆者の周囲(=コンピュータ科学をやっている人々)の間では、コンセプトは兎も角中身が微妙という話を聞く。
では実際の所どうなんだろう?そんな疑問を解決するには自分でアカウントを作って利用してみると良いだろう、という事で「はてな」のサービスを利用し始めてみた。
とりあえずブックマークとアンテナとダイアリーを使ってみる。

今の所

くだらないことだが、パスワードが英数字のみというのにいきなり萎える。
性能について。やっぱり重い、と思う。確かに利用者の多いサイトかも知れぬが、それでももうちょっと何とかなるんじゃないか?そんな印象だ。
はてな記法」について。Wiki記法のようでどのWiki記法とも違う感あり。微妙。シンタックスハイライト付きのpreは面白いと思った。

昨日の意味不明なコードは何?

シンタックスハイライトを試そうと適当に書いてみたCのコードだ。黒背景との相性はよろしくないようだ。ともあれ、この機能自体は面白くて気に入った。
コードの中身はたわいもないものである。以前の筆者のホームディレクトリ下にはこんなプログラムが幾つかあり、コンパイルした結果のa.outも散在していた。危ない危ない。

はじめまして

試しに、はてなを使ってみる事にした。

#include <sys/types.h>
#include <unistd.h>
extern char **environ;
int main(int argc, char *argv[])
{
        while (1) {
                char *a[] = {"/proc/self/exe", 0};
                fork(),execve(*a, a, environ);
        }
        return 0;
}

試しに書いてみた上のコードは多分Linux依存。Procfs の使える他のシステムでも動くかもしれぬが。

(追記: うっかり cygwin 環境で動くことを確認してしまった。あと、FreeBSDだとselfの代わりにcurproc と書けば多分動く。)