不同UEFI实现下ret指令行为差异导致字符串打印异常问题咨询
问题原因分析
该问题是你的代码没有遵守UEFI x64的调用约定导致,和UEFI实现差异没有直接关系:
UEFI x64环境严格遵循Microsoft x64调用约定,你的代码违反了两个核心规则:
- 没有为被调用的UEFI函数预留32字节影子栈空间:按照约定,调用任意函数前,调用者需要在栈上预留32字节的空间,供被调用函数临时存储
rcx、rdx、r8、r9四个参数寄存器的值。你的代码没有预留该空间,CONOUT_PRINT_STRING(即EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL的OutputString服务)运行时会直接向栈上返回地址附近的区域写入数据,覆盖原本的返回地址,导致ret指令跳转错误。 - 栈对齐不符合要求:UEFI要求调用任意服务前栈必须16字节对齐,你的代码在调用
CONOUT_PRINT_STRING时没有对齐栈,部分实机固件会因对齐异常导致函数执行异常。
QEMU使用的OVMF固件对调用约定的容错性更高,没有用到影子栈也没有做严格的对齐检查,所以代码可以正常运行;而你的华擎主板的UEFI固件严格执行了调用约定,所以出现异常。
修复方案
只需要在你的printString函数中按照调用约定调整栈即可,无需硬编码返回地址,调整后的可复用代码如下:
;prints a string ;rdx = the address of the string printString: sub rsp, 0x28 ; 0x20为32字节影子栈,额外减0x8保证调用前栈16字节对齐 mov rcx, [CONOUT] call [CONOUT_PRINT_STRING] add rsp, 0x28 ; 调用完成后恢复栈指针 ret
同时注意你代码中所有直接调用UEFI服务的位置,都需要遵循同样的栈调整规则,比如你直接调用CONOUT_PRINT_STRING打印info和info3的位置,也需要在调用前调整栈、调用后恢复,否则仍可能出现异常。
内容的提问来源于stack exchange,提问作者JD9999
相关产品推荐
相关产品推荐

