内嵌汇编实现string_cmp异常排查:始终返回1问题求助
内嵌汇编实现
string_cmp的问题排查与修复 尝试用内嵌汇编实现
string_cmp函数判断两个字符串是否相等,当前实现无论字符串是否相等都返回1,需排查问题,包括条件跳转指令的选择,以及非空字符串与空字符串对比的场景问题。
用户当前实现代码:
int string_cmp(const char* str1, char* str2) { int res; asm volatile ( "loop1:" "mov (%[s1]), %%rsi\n" // Move the source address to rsi "mov (%[s2]), %%rdx\n" //I wanted a name register but i couldnt initialsied rdx for some reason and %0 cannot be tested "inc %[s1]\n" "inc %[s2]\n" //check for end of a string then comp last char "test %%rsi, %%rdx\n" "jl neg\n" "jg pos\n" "cmp $0, %%rsi\n" "jne loop1\n" "je zero\n" "pos:" "mov $1, %%eax\n" "jmp fin\n" "zero:" "mov $0, %%eax\n" "jmp fin\n" "neg:" "mov $0xFFFFFFFF, %%eax\n" "jmp fin\n" "fin:" // Continue with the string copy using appropriate instructions : "=a" (res) : [s1] "r" (str1), [s2] "r" (str1) //need to initiate variables somewhere : "%rsi" // Use these as needed //clobbbered ); return res; }
问题根源分析
- 致命输入参数错误:输入操作数中
[s2] "r" (str1)把str2误写为str1,导致两个指针始终指向同一个字符串,这是返回结果异常的核心原因。 test指令误用:test %%rsi, %%rdx是对两个寄存器做按位与运算,仅能判断值是否为0,完全无法实现两个字符的大小比较,必须用cmp指令替代。- 寄存器宽度不匹配:
mov (%[s1]), %%rsi将8位的char加载到64位寄存器rsi中,高位残留的旧数据会干扰后续判断,应改用8位寄存器(如sil、dl)存储字符。 - 条件跳转逻辑错误:
test指令之后用jl/jg跳转完全错误,这类指令基于有符号数大小判断,但test的结果仅影响零标志、符号标志,无法反映两个字符的大小关系。正确做法是用cmp指令后搭配对应跳转:- 无符号字符(符合C标准
strcmp的默认行为):用jb(小于跳转)、ja(大于跳转); - 有符号字符:用
jl(小于跳转)、jg(大于跳转)。
- 无符号字符(符合C标准
- 指针递增时机错误:先递增指针再判断字符串结束,会跳过最后一个空字符,无法正确识别两个字符串同时结束的场景。
- Clobber列表不全:代码中修改了
rdx但未在Clobber列表声明,且s1/s2被inc修改,应使用"+r"(输入输出操作数)而非"r",否则编译器可能优化掉修改操作。
修复后的代码
int string_cmp(const char* str1, const char* str2) { int res; asm volatile ( "loop1:\n" // 加载8位字符到低8位寄存器,避免高位数据干扰 "movb (%[s1]), %%al\n" "movb (%[s2]), %%dl\n" // 先比较当前字符 "cmp %%dl, %%al\n" // 字符不等时跳转分支 "jb neg\n" "ja pos\n" // 判断是否到达字符串末尾 "test %%al, %%al\n" "jz zero\n" // 未结束则递增指针继续循环 "inc %[s1]\n" "inc %[s2]\n" "jmp loop1\n" "pos:\n" "mov $1, %%eax\n" "jmp fin\n" "zero:\n" "mov $0, %%eax\n" "jmp fin\n" "neg:\n" "mov $-1, %%eax\n" // 直接用$-1更清晰,等价于0xFFFFFFFF "jmp fin\n" "fin:\n" : "=a" (res), [s1] "+r" (str1), [s2] "+r" (str2) // s1/s2被修改,使用+r标记 : // 无额外输入参数 : "%dl" // 声明被修改的寄存器 ); return res; }
修复说明
- 修正输入参数错误,让
s2正确绑定str2; - 改用8位寄存器存储字符,避免高位数据干扰;
- 调整逻辑顺序:先比较字符,再判断是否到达字符串末尾,符合正常的字符串比较流程;
- 用
cmp指令实现字符比较,搭配jb/ja完成无符号大小判断,对齐C标准strcmp的行为; - 用
"+r"标记被修改的指针变量,通知编译器变量会被修改; - 用
$-1替代$0xFFFFFFFF,提升代码可读性。
内容的提问来源于stack exchange,提问作者shinny.dogma
相关产品推荐
相关产品推荐

