匯編中使用call指令的時(shí)候, 需要注意棧的平衡
函數(shù)棧平衡, 保證函數(shù)調(diào)用前后,sp是一致的
- 1.外平棧: 由函數(shù)外部保持棧平衡
- 2.內(nèi)平棧: 由函數(shù)內(nèi)部保持棧平衡
assume cs:code, ds:data, ss:stack
stack segment
db 20 dup(1) ; 定義20個(gè)字節(jié)作為棧段
stack ends
data segment
db 20 dup(2)
str db 'Hello world!$'
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, stack
mov ss, ax
push 1
push 2
call sum ;相當(dāng)于函數(shù)調(diào)用
; add sp, 4 ;可以在函數(shù)調(diào)用完畢進(jìn)行平棧,外平棧
mov ax, 4ch
int 21h
sum:
mov bp, sp ;將sp賦值給bp寄存器,bp是專門(mén)用來(lái)代替sp進(jìn)行尋址用的寄存器
sub sp, 20; 預(yù)留N個(gè)字節(jié)的空間給局部變量
mov ss:[bp - 2], 3
mov ss:[bp - 4], 4 ;局部變量粗如棧預(yù)留空間中
mov ax, ss:[bp + 2]
add ax, ss:[bp + 4]
add ax, ss:[bp - 2]
add ax, ss:[bp - 4] ;取出局部變量
mov sp, bp ;將sp回到下一條指令的地址所存放的站地址
ret 4 ;在函數(shù)內(nèi)部 ret將下一條指令的地址pop完畢后進(jìn)行平棧, 內(nèi)平棧
code end
end start
函數(shù)的局部變量
- 當(dāng)函數(shù)內(nèi)部聲明變量的時(shí)候, 棧會(huì)有一部分預(yù)留空間(即sp - N)來(lái)給局部變量使用, 防止函數(shù)外部push的時(shí)候, 覆蓋局部變量
- 取參數(shù)的時(shí)候是bp + N(N為2的倍數(shù)), 取局部變量的時(shí)候是 bp - N
問(wèn)題1: 在函數(shù)中調(diào)用函數(shù), 按照上邊的理解, bp會(huì)指向下一個(gè)函數(shù)棧空間中的指令地址,那么ret后, 怎么平棧呢?
- 所以進(jìn)入函數(shù)的第一件事情, 就是保護(hù)bp寄存器, push bp, 保護(hù)上一個(gè)函數(shù)的地址
問(wèn)題2: 在函數(shù)內(nèi)部修改了寄存器中的值, 那么別的地方需要用到, 怎么保證數(shù)據(jù)安全呢?
- 這就是我們所說(shuō)的破壞現(xiàn)場(chǎng)
- 需要我們?cè)陂_(kāi)辟預(yù)留空間之后, 將寄存器push入棧, 保護(hù)寄存器
- 在業(yè)務(wù)邏輯代碼完成之后, pop 寄存器, 將入棧的數(shù)據(jù)pop給寄存器
sum:
push bp
mov bp, sp ;將sp賦值給bp寄存器,bp是專門(mén)用來(lái)代替sp進(jìn)行尋址用的寄存器
sub sp, 20; 預(yù)留N個(gè)字節(jié)的空間給局部變量
;**************業(yè)務(wù)邏輯
mov ss:[bp - 2], 3
mov ss:[bp - 4], 4 ;局部變量粗如棧預(yù)留空間中
mov ax, ss:[bp + 2]
add ax, ss:[bp + 4]
add ax, ss:[bp - 2]
add ax, ss:[bp - 4] ;取出局部變量
;******************業(yè)務(wù)邏輯
mov sp, bp ;恢復(fù)sp
pop bp
ret 4 ;在函數(shù)內(nèi)部 ret將下一條指令的地址pop完畢后進(jìn)行平棧, 內(nèi)平棧
以下的匯編代碼是一個(gè)函數(shù)的標(biāo)準(zhǔn)開(kāi)頭和結(jié)束
push bp
mov bp, sp ;將sp賦值給bp寄存器,bp是專門(mén)用來(lái)代替sp進(jìn)行尋址用的寄存器
sub sp, 20; 預(yù)留N個(gè)字節(jié)的空間給局部變量
..........
mov sp, bp ;恢復(fù)sp
pop bp
ret 4 ;
函數(shù)的調(diào)用流程
- 1: push 參數(shù)(64位CPU 寄存器多, 不需要push入棧)
- 2: call指令將下一條指令地址入棧
- 3: 保護(hù)bp寄存器, mov bp, sp
- 4: 提升sp, 作為局部變量的占空間(sp - N(N為2倍數(shù)))
- 5: 保護(hù)寄存器,(當(dāng)外部和函數(shù)內(nèi)部都用到該寄存器時(shí))
- 6: 業(yè)務(wù)邏輯代碼
- 7: 恢復(fù)寄存器 pop bx
- 8: 恢復(fù)sp mov sp, bp 或者 sp + N
- 9: 恢復(fù)bp pop bp
- 10: ret pop sp pop下一個(gè)指令的地址值