如何将x86-64汇编aBin2int宏改写为符合调用约定的标准函数
aBin2int宏改写为标准约定函数适配方案
原始可用宏代码
此前编写的可正常运行的aBin2int宏代码如下:
%macro aBin2int 2 push rbp mov rbp, rsp sub rsp, 8 push rcx push rdx mov dword [%2], 0 mov dword [rbp - 4], 0 mov dword [rbp - 8], 2 mov rax, 0 mov rdx, 0 mov rcx, 0 mov r10, 0 %%nxtCR: movzx eax, byte [%1 + r10] cmp eax, NULL je %%controlDone cmp eax, "0" jae %%adding %%adding: sub eax, "0" mov dword [rbp - 4], eax mov eax, dword [%2] mul dword [rbp - 8] add eax, dword [rbp - 4] mov dword [%2], eax inc r10 jmp %%nxtCR %%completed: pop rdx pop rcx mov rsp, rbp pop rbp %endmacro
需求背景
当前需要实现多层函数调用逻辑,首先编写getIterations()函数,负责参数错误校验、ASCII格式二进制字符串转整数,命令行参数固定格式为"-it <binaryNumber> -rs <binaryNumber>",函数定义如下:
; ****************************************************************** ; Function getIterations() ; Performs error checking, converts ASCII/binary to integer. ; Command line format (fixed order): ; "-it <binaryNumber> -rs <binaryNumber>" ; ----- ; Arguments: ; 1) ARGC, double-word, value ; 2) ARGV, double-word, address ; 3) iterations count, double-word, address ; 4) rotate spped, double-word, address
getIterations()调用字符串转整数函数的约定参考同项目ternary2int:高级语言调用形式为bool = ternary2int(&str, &int);,寄存器传参规则为:1)待转换字符串地址存入rdi,2)整数存储地址存入rsi;返回规则为:通过传入地址回写转换结果,函数本身返回布尔值,转换合法返回TRUE,出错返回FALSE,ternary2int定义如下:
; ******************************************************************************* ; Simple function to convert ternary string to integer. ; Reads string and converts to intger ; ----- ; HLL Call ; bool = ternary2int(&str, &int); ; ----- ; Arguments passed: ; 1) string, address, rdi ; 2) integer, address, rsi ; ----- ; Returns ; 1) integer value (via passed address) ; 2) bool, TRUE if valid conversion, FALSE for error
现存问题
aBin2int宏版本运行正常,但改写为符合上述调用约定的独立函数时运行异常。当前已编写的二进制合法性校验、aBin2int调用代码如下:
mov rbx, qword[rsi + 16] mov r10, 0 binaryCheck: mov rax, 0 mov al, byte[rbx + r10] cmp al, NULL je convertBinary cmp al, "2" jae itsNoBinary cmp al, "0" jb itsNoBinary inc r10 jmp binaryCheck convertBinary: mov r15, 0 mov rdi, rbx mov rsi, r15 call aBin2int
问题定位与修正
核心错误点
- 传参逻辑错误:调用
aBin2int前执行mov r15, 0再mov rsi, r15,相当于给第二个参数传入了空地址0,函数向0地址写入结果会直接触发段错误,此处必须传入目标整数变量的有效内存地址。 - 宏本身存在隐性bug:原宏中
cmp eax, "0"后的jae %%adding是无效跳转,无论比对结果如何都会直接进入转换逻辑,没有做字符合法性校验,且循环退出标签%%controlDone未定义,宏版本因为逻辑巧合未触发问题,改写函数时必须修正。 - 未遵循x86-64调用约定:独立函数需要保存
rbx、rbp、r12-r15这类非易失寄存器,原宏仅保存了rcx、rdx,改写后如果不补全寄存器保存逻辑,会破坏上层函数的寄存器值导致运行异常。 - 字符校验逻辑不严谨:现有校验逻辑只判断字符小于"0"、大于等于"2"两种异常,漏了字符为"1"以上、"2"以下的合法判断,会把非0/1的非法字符放过。
修正后的aBin2int函数实现
; 调用约定:rdi = 二进制字符串首地址, rsi = 整数存储内存地址 ; 返回值:rax = 1(TRUE)转换成功, rax = 0(FALSE)转换失败 aBin2int: push rbp mov rbp, rsp push rbx push rcx push rdx push r10 mov dword [rsi], 0 ; 初始化结果为0 mov r10, 0 ; 字符串遍历索引 mov ecx, 2 ; 二进制基数 conv_loop: movzx eax, byte [rdi + r10] cmp eax, NULL je conv_success ; 遍历到字符串结束符,转换完成 ; 校验当前字符是否为合法二进制位 cmp eax, "0" jb conv_fail cmp eax, "1" ja conv_fail sub eax, "0" ; ASCII字符转数值0/1 mov ebx, eax ; 暂存当前位 mov eax, dword [rsi] mul ecx ; 结果 = 原结果 * 2 add eax, ebx ; 累加当前位 mov dword [rsi], eax ; 回写结果 inc r10 jmp conv_loop conv_fail: mov rax, 0 jmp conv_exit conv_success: mov rax, 1 conv_exit: pop r10 pop rdx pop rcx pop rbx pop rbp ret
调用逻辑修正
调用aBin2int时,必须将目标变量的地址传入rsi,不能传立即数0,修正后的调用片段示例:
; 此处省略ARGC数量校验、"-it"/"-rs"选项字符串匹配逻辑 ; rsi为传入的ARGV数组首地址 mov rbx, qword [rsi + 16] ; 取argv[2],即-it对应的二进制字符串 mov r10, 0 binaryCheck_it: mov al, byte [rbx + r10] cmp al, NULL je doConv_it cmp al, "1" ja arg_err cmp al, "0" jb arg_err inc r10 jmp binaryCheck_it doConv_it: mov rdi, rbx mov rsi, qword [rbp + 16] ; 传入iterations变量的内存地址,而非0值 call aBin2int test rax, rax jz arg_err ; 转换失败跳转错误处理 ; -rs参数的校验、转换逻辑与上述逻辑一致,取argv[4]传入即可
内容的提问来源于stack exchange,提问作者Jawj
相关产品推荐
相关产品推荐

