2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スタックアセンブリ:Hello World without libc

Posted at

"依存を排せ。世界を構築するとは、自らの足元を知ることから始まる。"

あなたの書いた「Hello, World!」は、
本当にあなたのものだろうか?

それは、Cランタイムが整えた世界の上に、
無数の関数と抽象が重ねられた結果にすぎないのではないか?

この章は、そのすべてを捨てることから始まる。

libcに依らず、リンカに依らず、OSすら最小限に頼り、
スタックとレジスタのみによって世界を構築する

それが「スタックアセンブリ」による、最小の“Hello, World”。


前提:libc なしとはどういうことか

通常、C言語で書かれたプログラムは、以下に依存している:

  • スタートアップルーチン(_startmain
  • 標準入出力ライブラリ(printf, puts
  • メモリアロケーション(malloc, free
  • グローバル変数の初期化と破棄

つまり、int main()を書いた時点で、
すでに100KB以上の依存物を呼び出している

この章の目標は:

  • これらをすべて排除する
  • 自分の手で「開始」と「終了」を設計する
  • 「文字を表示する」とは物理的に何が起きているかを理解する

ということにある。


方法:最小構成での write syscall 利用

Linuxにおいて、write(1, buffer, size) は、
標準出力に任意のバイト列を書き出すことを意味する。

これを使えば、Cもprintfも不要だ。

必要な構成要素:

  • .textセクション:エントリポイント_start
  • .dataセクション:表示するメッセージ
  • rax/rdi/rsi/rdxのレジスタ配置
  • syscall命令
  • exitの明示

コード例:libc なし “Hello, world!”

section .data
    msg db "Hello, world!", 0xA
    len equ $ - msg

section .text
    global _start

_start:
    ; write(1, msg, len)
    mov     rax, 1      ; syscall: write
    mov     rdi, 1      ; file descriptor: stdout
    mov     rsi, msg    ; pointer to message
    mov     rdx, len    ; length of message
    syscall

    ; exit(0)
    mov     rax, 60     ; syscall: exit
    xor     rdi, rdi    ; status: 0
    syscall

ビルドと実行

nasm -f elf64 hello.asm -o hello.o
ld -o hello hello.o
./hello

これで、完全に libc 非依存の Hello World が完成する。


スタックはどう使われているのか?

ここであえてスタック操作を導入してみよう。

section .text
    global _start

_start:
    sub     rsp, 32     ; スタック確保(アライメント確保)
    mov     rax, 1
    mov     rdi, 1
    lea     rsi, [rel msg]
    mov     rdx, msglen
    syscall

    mov     rax, 60
    xor     rdi, rdi
    syscall

section .data
    msg db "Hello from the stack.", 0xA
    msglen equ $ - msg

ここで重要なのは、
**“スタックを安全に操作しながら syscall を行う”**という点。

  • sub rsp, 32syscall 前の16バイト境界アライメントの慣例
  • lea rsi, [rel msg] によってセグメント超えを防ぐ

スタックの設計は、
単なる退避領域ではない。
それは**「一時的な宇宙」**であり、制御とデータの両方を包摂する構造だ。


さらに一歩:msg 自体をスタック上に構築する

section .text
    global _start

_start:
    sub     rsp, 32
    mov     rbx, rsp
    mov     dword [rbx], 0x0A21646C  ; 'ld!\n'
    mov     dword [rbx+4], 0x726F7720 ; ' wor'
    mov     dword [rbx+8], 0x6F6C6C65 ; 'ello'

    mov     rax, 1
    mov     rdi, 1
    mov     rsi, rbx
    mov     rdx, 13
    syscall

    mov     rax, 60
    xor     rdi, rdi
    syscall

この例では、文字列ですらデータセクションを使わず、スタック上に構築している
これは、“自己完結性”の極致であり、
**「構造の中に意味を畳み込む」**という設計思想の表現である。


スタックアセンブリがもたらすもの

  1. 理解の透明性

    • 何が使われ、何が破棄され、何が残るのかすべてが見える
  2. 最小限の設計哲学

    • 使わないものは持ち込まない。書かないことが構造になる。
  3. 自己完結性の美学

    • メモリ空間、実行単位、終了まで、すべて自前で用意できる
  4. ライブラリとランタイムからの独立

    • 世界を構築する“最初の設計者”としての立場を取り戻せる

結語:Hello Worldは、世界の宣言である

printf("Hello, world!"); は便利だ。
だがその便利さは、すべてを隠すという代償の上に成り立っている。

スタックアセンブリは、
その隠された全てを、一命令ずつ明かしていく設計行為である。

  • 世界を知るために
  • 意志を刻むために
  • 設計とは何かを取り戻すために

あなたの「Hello」は、自分の設計であるべきだ。

"Hello, world." それは、世界の上に自分が立ったことを、たった一度だけ証明する言葉である。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?