在過去的幾天里,我一直在使用 x8664 程式集(在 macOS 上使用 nasm)感到痛苦。我想展示兩段代碼
所以假設我有一個陣列,我想列印它。這是我得到的代碼,
_main:
push rbp
mov rbp, rsp
lea rdi, array
mov rsi, length
call _printy
pop rbp
mov rdi, 0
call _exit
_printy:
push rbp
mov rbp, rsp
sub rsp, 16
xor rbx, rbx
mov r12, rdi
mov r15, rsi
.loop:
cmp rbx, r15
jge .done
lea rdi, format
mov rsi, [r12 rbx * 4]
xor rax, rax
call _printf
add rbx, 1
jmp .loop
.done:
leave
ret
簡要說明:
- 在 rdi 中傳遞陣列,在 rsi 中傳遞長度
- 呼叫 _printy
- 為 printf 呼叫創建 16 位元組對齊的堆疊幀,將資料保存在保留的暫存器中。
- 回圈直到條件成立并離開
- 然后退出
這是痛苦的部分,請考慮退出部分的以下代碼
call printy(char const*)
mov eax, 0
pop rbp
ret
這是編譯器資源管理器生成的代碼。如果我嘗試復制它, printf 會慘遭失敗。與我得到的除錯器一起步進:
thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0xe)
frame #0: 0x0000000100015501 dyld`start 465
dyld`start:
-> 0x100015501 < 465>: mov rax, qword ptr [rbx 0x8]
0x100015505 < 469>: mov edi, dword ptr [rax 0x34]
0x100015508 < 472>: xor esi, esi
0x10001550a < 474>: call 0x100041a26 ; dyld3::MachOFile::isSimulatorPlatform(dyld3::Platform, dyld3::Platform*)
我個人無法理解這次崩潰背后的原因,也無法理解使用 _exit 函式是否成功,所以這是有人可以為我解決的第一個疑問。
我的第二個問題是關于暫存器大小。這段代碼比第一個更好嗎?
_main:
push rbp
mov rbp, rsp
lea rdi, array
mov esi, length
call _printy
pop rbp
mov rdi, 0
call _exit
_printy:
push rbp
mov rbp, rsp
sub rsp, 16
xor ebx, ebx
mov r12, rdi
mov r15d, esi
.loop:
cmp ebx, r15d
jge .done
lea rdi, format
mov esi, [r12 rbx * 4]
xor rax, rax
call _printf
add ebx, 1
jmp .loop
.done:
leave
ret
如您所見,我正在加載esi而不是rsi使用ebxand r15d。我感興趣的行是
mov esi, [r12 rbx * 4]
使用 esi 更好嗎(我正在處理整數陣列)?或者使用 rsi 并沒有太大區別。通過使用 esi,我將存盤到 rsi 暫存器的低 4 個位元組中,在使用 rsi 本身時,我消耗了所有 8 個位元組。是否有某種性能影響?在一天結束時,我猜這個數字將是零擴展的,無論你保存 4 位元組還是 8 位元組,rsi 暫存器都不能保存另一個整數。那么這個類似的操作呢?movsx rsi, dword[r12 rbx * 4]現在我很明確(我是說我要存盤 4 個位元組,并且我正在擴展符號。這比直接使用 4 位元組暫存器更好嗎?或者它非常類似于
rsi, [r12 rbx * 4]? 如果你能解決我的一些疑問,我會非常高興。感謝您的關注。
uj5u.com熱心網友回復:
我將回答標題中的問題:
ret簡單和_exit功能的區別
ret用于回傳給呼叫者,while_exit是請求作業系統立即終止程式,因此它不會執行進一步的指令。
你什么時候可以使用retvs. call exit()?您可以exit()在任何時候呼叫,無論呼叫鏈有多深,它都會終止程式,而不會給普通函式呼叫者執行或清理的機會。如果呼叫堆疊上的東西main不僅僅是例如列印錯誤訊息并停止程式)。
ret可用于回傳呼叫者,假設您已將堆疊清理到與函式進入時相同的狀態 - 換句話說,回傳地址是堆疊上的頂部。(您還必須恢復呼叫保留的暫存器,否則呼叫者可能無法作業。)
main()通常是_startcrt0.o中呼叫的函式,其中_start是默認程式入口點。
但是,_main(或任何其他(外部可見的)符號)可以直接設定為程式入口點,只要在程式構造中給出正確的聯結器選項。
程式入口點是新程式中執行的第一條指令,它不是作為函式被作業系統呼叫,而只是轉移控制,這意味著沒有回傳地址(在堆疊上),并且有沒有引數(至少,通常不遵循常規呼叫約定)。因為程式入口點不是作為真正的函式呼叫的,所以它也不能“回傳”給它的呼叫者(沒有程式入口點的呼叫者)。因此,程式入口點——例如,如果_start通過回傳獲得控制權main()——必須呼叫exit()適當的程式終止。(如果選擇了備用程式入口點,這同樣適用:它不能回傳,必須使用exit()或其他方式來終止程式。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/496695.html
下一篇:片段中的TextWatcher
