基礎知識
以下の話はx86アーキテクチャ依存の話が含まれているので、それ以外のアーキテクチャでは異なる場合もあります。
機械語・アセンブラ
コンピュータは当然プログラムのソースコードをそのまま理解できるわけではなく、2進数で示された命令を実行することしかできない。
https://gyazo.com/bab2726e1e9382a167a2535c59085a64
(C言語のHelloWorldプログラムをvimのバイナリモードで開いた図)
これを自力で書くのはしんどいので、2進数(実際は16進数を用いることが多いが)の命令と一対一で対応するように作られたのがアセンブリ言語である。
code:hello.s(上記の機械語と対応するアセンブラ)
.file "hello.c"
.text
.section .rodata
.LC0:
.string "helloworld"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 10.2.0"
.section .note.GNU-stack,"",@progbits
レジスタについて
アセンブリのコードでは値を格納するのにレジスタを利用することができる。レジスタはプロセッサが内蔵しているフリップフロップ回路などで実装された装置で、現代のコンピュータなら大体64bitの値を格納できる。レジスタは数個しか存在しないので格納できる値は多くないが、その分メモリ(一次記憶装置)より高速にアクセスすることができる。
レジスタはそれぞれに名前がついていて、特定の役割に用いるのが推奨されている。
例えば、算術演算の結果を格納するときはraxを用いる。
メモリについて
レジスタに比べるとアクセス時間は大幅に増加するが、CPUから直接アクセスすることができる。
プロセスごとに異なるメモリ空間が与えられ、それぞれ大きく分けて次のような領域に分かれている。
https://gyazo.com/8ed5cf4401d1ec095f224ce410999b8d
テキスト領域:実行するプログラムのバイナリが置かれる。
静的領域:プログラムが終了するまで存在するデータ(ソースコード中の文字列リテラル、C言語で言うstaticで指定された変数や初期化が行われているグローバル変数など)が置かれる。
ヒープ領域:mallocで取得できるメモリ領域はこのヒープ領域を使う。プログラム中で動的に確保したり解放したりすることができる。
スタック領域:関数呼び出し時にどこにreturnするかなどの情報や関数内のローカル変数などを積み上げる。文字通りスタックデータ構造のように利用でき、push, pop命令で操作できる。