何时需要Shadow Space?为何未分配该空间代码仍无报错?
X64调用约定中未分配Shadow Space却正常运行的原因
首先明确:微软X64调用约定确实要求调用者为被调用者预留32字节的Shadow Space,你当前代码能正常运行属于未定义行为下的巧合情况,并非符合规范的写法,具体原因如下:
被调用函数的实现细节:
printf这类CRT函数在禁用优化的编译环境下,内部可能并没有使用Shadow Space来保存RCX/RDX/R8/R9这些参数寄存器的值。只有当被调用函数需要将这些寄存器的值写入栈上的Shadow Space时,才会依赖调用者的预留操作;如果函数内部没做这个操作,自然不会触发错误。初始栈的空闲空间:
你的main作为程序入口,在调用_CRT_INIT之后,栈顶下方通常还有一段未被使用的合法栈内存。即使你没显式分配Shadow Space,被调用函数如果偶然访问到这段空闲栈内存,也不会触发内存访问违规。但这种空闲空间的大小是不确定的,换个环境、修改CRT初始化逻辑或者增加其他栈操作后,就可能出现崩溃。优化开关的影响:
禁用全程序优化后,CRT函数的实现会更偏向于“直白”的逻辑,不会为了性能去利用Shadow Space做寄存器优化。一旦开启优化,CRT函数的实现可能会主动使用Shadow Space来保存寄存器,此时未预留空间的代码必然会触发内存错误。
符合规范的正确写法
必须显式分配Shadow Space,示例代码修改如下:
includelib msvcrt.lib includelib legacy_stdio_definitions.lib extern _CRT_INIT: proc extern printf: proc extern ExitProcess: proc .data msg db "hello world!", 13, 10, 0 .code main proc call _CRT_INIT sub rsp, 32 ; 为被调用者分配32字节Shadow Space lea rcx,msg call printf add rsp, 32 ; 调用完成后释放Shadow Space call ExitProcess main endp end
内容的提问来源于stack exchange,提问作者Mohammad
相关产品推荐
相关产品推荐

