1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

x86-64 実験:自作 push/pop でスタックの仕組みを体感する

1
Posted at

目的

アセンブリのpop,pushを使わず別の命令で再現することでスタックへの理解を深めることが目的です。

pushとは

レジスタの値をスタックに積む。
rspレジスタが常にスタックの一番上を指している。

push rax

は以下のような処理をする

mov rax, [str2]
sub rsp, 8
mov [rsp], rax

mov rax, [str2]

str2が指している文字列のアドレス(8バイト)をraxに格納
例:RAX = 0x0000000000002000 (8バイトなので0埋め)

sub rsp, 8

rspはスタックに値が積まれると低い方へ伸びる。値を積む為に8引き領域を確保する。
例:RSP = 0x1000 → 0x0FF8

mov [rsp], rax

rspの指しているアドレスへ値を格納する。
例:
0x0FF8 → 0x00 (RAX 下位バイト)
0x0FF9 → 0x20
0x0FFA → 0x00
0x0FFB → 0x00
0x0FFC → 0x00
0x0FFD → 0x00
0x0FFE → 0x00
0x0FFF → 0x00 (RAX 上位バイト)
※低アドレスから高アドレス に下位バイト → 上位バイト

popとは

スタックから値を1つ取り出す。

pop rbx

は以下のような処理をする

mov rbx, [rsp]
add rsp, 8 

mov rbx, [rsp]

スタックの値を取り出しrbxレジスタへ格納

例:
取り出した直後はこのようになっている
アドレス 値
0x0FF8 0x00 ← RAX 下位バイト
0x0FF9 0x20
0x0FFA 0x00
0x0FFB 0x00
0x0FFC 0x00
0x0FFD 0x00
0x0FFE 0x00
0x0FFF 0x00 ← RAX 上位バイト
RSP = 0x0FF8

add rsp, 8

スタックポインタを元に戻して領域を開放
RSP = 0x1000
これで元通り!

実装

test@test-ThinkPad-X280:~/test/stack$ nasm -f elf64 stack.asm -o stack.o
test@test-ThinkPad-X280:~/test/stack$ ld -o stack stack.o
test@test-ThinkPad-X280:~/test/stack$ ./stack
test.asm
global _start ; _startから実行を始める

section .data
    str1    dq str1_str
    str2    dq str2_str
    str3    dq str3_str
str1_str db "Hello", 10, 0
str2_str db "World", 10, 0
str3_str db "Stack!", 10, 0

section .text

; ------------------------------------------------
; 文字列の長さ(バイト数)を数える
; ------------------------------------------------
strlen:
    push rdi
    xor rax, rax ; バイト数勘定用レジスタ初期化
.len_loop:
    cmp byte [rdi + rax], 0 ; 終端文字0かどうか
    je .len_done ; 0ならば処理を終了
    inc rax ; rax++
    jmp .len_loop
.len_done:
    pop rdi
    ret

; ------------------------------------------------
; 文字列を画面出力
; ------------------------------------------------
print_str:
    ; レジスタ退避
    push rdi
    push rsi
    push rdx
    push rcx

    mov rdi, rax   ; 書き込む文字列のアドレス
    call strlen    ; バイトが返る

    mov rsi, rdi   ; 書き込む文字列のアドレス
    mov rdx, rax   ; 書き込むバイト数
    mov rax, 1     ; writeシステムコール番号1
    mov rdi, 1     ; 書き込み先=標準出力(画面)
    syscall

    ; レジスタ復元
    pop rcx
    pop rdx
    pop rsi
    pop rdi
    ret

; ------------------------------------------------
;  実験コード
;  A) 標準 push → 自作 pop
;  B) 自作 push → 標準 pop
;  C) 自作 push + 自作 pop
; ------------------------------------------------
_start:
    and rsp, 0xFFFFFFFFFFFFFFF0 ; 下位4ビットを0にして16の倍数にする

    ; ---------- A: 標準 push -> 自作 pop  ----------
    mov rax, [str1] ; 文字列1のアドレスをpush
    push rax
    
    mov rbx, [rsp] ; (自作POP) スタックから値を取り出しrbxへ格納 
    add rsp, 8 ; 8足してスタックを8バイト分開放
    
    mov rax, rbx ; write()呼び出し
    call print_str

    ; ---------- B: 自作 push -> 標準 pop ----------
    mov rax, [str2]
    sub rsp, 8 ; 8引いて8バイト分領域を確保
    mov [rsp], rax ; (自作push) 文字列2のアドレスをスタックへ値を積む

    pop rax
    call print_str 

    ; ---------- C: 自作 push + 自作 pop (both inline) ----------
    mov rax, [str3] ; push
    sub rsp, 8
    mov [rsp], rax
    
    mov rbx, [rsp] ; pop
    add rsp, 8
    
    mov rax, rbx ;write
    call print_str

    ; return 0
    mov rax, 60  ; exitのシステムコール番号
    xor rdi, rdi ; 返り値
    syscall

実行結果

test@test-ThinkPad-X280:~/test/stack$ ./stack
Hello
World
Stack!
1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?