x64汇编中函数栈参数双重解引用的合法性与逻辑问询
疑问1:lfunc中RBX未按调用约定备份却能运行
Microsoft x64调用约定规定,RBX属于非易失性寄存器,函数使用前必须保存(push到栈)、使用后恢复(pop),以保证调用者的RBX值不受影响。
这段代码能正常运行的原因是:调用者main在调用lfunc后,没有再使用RBX寄存器。即使lfunc修改了RBX,也不会破坏后续逻辑。但这属于不规范的写法,严格遵守调用约定的话,应该在lfunc开头添加push rbx,结尾添加pop rbx。
疑问2:栈参数读取的逻辑与合法性
你这里的核心误解是混淆了变量标签的含义:在汇编中,fourth是一个内存标签,它代表的是存储字符'D'的内存地址,而不是字符'D'的值(0x44)。
栈布局分析
在main调用lfunc前,执行了push fourth等操作,这些push指令压入栈的是各个字符变量的地址(64位,8字节),而不是字符值。进入lfunc后,栈帧的关键布局如下:
rbp+8:调用lfunc的返回地址(8字节)rbp+8+8到rbp+8+8+31:32字节的shadow space(Microsoft x64调用约定要求的)rbp+8+8+32:栈上第一个额外参数,也就是fourth的地址(8字节)
所以代码中mov rax, qword [rbp+8+8+32]是正确的:用qword读取是因为x64的地址是64位,栈上每个push的参数都是8字节的地址。之后mov bl,[rax]是从fourth的地址中读取字节,也就是字符'D'的0x44,完全合法,自然能得到正确结果。
你之前误以为压入栈的是0x44,所以才会觉得解引用是垃圾地址,实际上压入的是变量的地址,解引用后就是正确的字符值。
调试问题说明
断点导致调试器挂起大概率是调试环境的问题,比如io64.inc中的代码可能包含调试器不兼容的指令,或者调试器本身的配置问题。建议尝试移除io64.inc后重新编译调试,或者换用其他调试工具测试。
内容的提问来源于stack exchange,提问作者Duke William

