C调用64位汇编sign函数时int参数符号丢失问题
问题背景
尝试在64位汇编环境下实现符号函数sign供C代码调用,函数返回值为int类型,判定规则如下:
- 参数为负数时返回-1
- 参数为0时返回0
- 参数为正数时返回1
故障现象
程序可正常识别正数与0,但传入负数时会被误判为正数,最终返回结果1。
附现有代码
C侧代码 main.c
#include <stdio.h> extern int sign(signed int); int main() { signed int num; scanf("%d",&num); printf("num: %d sign: %d\n",num,sign(num)); return 0; }
汇编侧代码 sign.asm
global sign section .text sign: test rdi,rdi js neg_lab je zero_lab mov rax,1 jmp end_lab neg_lab: mov rax,-1 jmp end_lab zero_lab: xor rax,rax end_lab: ret
前期排查时手动给待检测寄存器分别赋值正数、负数、零测试,汇编逻辑可正确识别三类数值,负数判定正常,因此初步判断问题出在参数传递环节。
故障根因
手动测试的结果不具备参考性,因为手动给寄存器赋值时没有复现C调用的真实传参状态。
在遵循System V AMD64调用约定的64位环境下,C语言传入32位int类型参数时,仅会写入对应传参寄存器的低32位,不会自动将符号位扩展到64位。传入负数时,低32位为对应负数的补码表示,高32位全为0,此时整个64位rdi寄存器存储的是正数值,原有代码中test rdi, rdi检测的是64位整数值的符号位(最高位),无法触发js负跳转,最终会走到返回1的正数分支。
以传入int类型的-1为例,传参完成后rdi的实际值为0x00000000FFFFFFFF,64位视角下最高位为0,符号标志位SF不会置位,负跳转自然不生效。
修复方案
两种方案任选其一即可:
- 调整检测位宽匹配参数类型,仅判断低32位寄存器,同时返回值也只操作低32位(x86-64下写入32位寄存器会自动将对应64位寄存器高32位清零,符合返回值规范):
global sign section .text sign: test edi,edi ; 检测低32位,对应传入的32位int参数 js neg_lab je zero_lab mov eax,1 jmp end_lab neg_lab: mov eax,-1 jmp end_lab zero_lab: xor eax,eax end_lab: ret
- 函数入口处先对传入的32位参数做符号扩展,扩展为64位值后再走原有判断逻辑,只需要在
sign标签后第一行添加指令movsxd rdi, edi即可,其余代码无需改动。
内容的提问来源于stack exchange,提问作者Gábor
相关产品推荐
相关产品推荐

