x86-64平台reduce高阶函数汇编实现段错误排查求助
我需要在x86-64平台实现一个符合System V ABI调用约定的reduce高阶函数,功能对应如下C语言代码:
unsigned reduce( unsigned (*fnct_ptr) (unsigned param_one, unsigned param_two), unsigned init, size_t len, const unsigned arr[]){ for (int i = 0; i < len; i++) { init = fnct_ptr(init, arr[i]); } return init; }
该函数接收双参数函数指针、初始值、数组长度和数组作为参数,我编写的汇编代码如下,但调用时出现段错误,怀疑是32位值与64位寄存器的地址大小不匹配导致,请求排查问题:
reduce: XOR rbx, rbx ; set counter to zero MOV rsp, rdi ; fnctPntr -> rsp MOV rax, rsi ; init -> rax while: CMP rbx, rdx ; if counter >= lengrh -> end JGE end MOV rdi, rax ; load parameters to rdi and rsi MOV rsi, [rcx+rbx*8] CALL rsp ; call function with pointer INC rbx JMP while end: RET
错误排查与修复
你的代码存在三个核心问题,其中第一个是导致段错误的直接原因:
错误1:非法修改栈指针rsp
rsp是x86-64中维护栈结构的专用寄存器,你把函数指针从rdi移到rsp,直接破坏了栈的正常布局。调用CALL rsp时,CPU会把返回地址压入当前rsp指向的位置(也就是你存的函数指针地址),这会直接覆盖函数指针,同时后续栈操作完全混乱,必然触发段错误。
修复:用非易失性通用寄存器存储函数指针,比如r12(System V ABI中r12-r15是调用者保存寄存器,需要先保存到栈),避免破坏rsp。错误2:数组元素访问宽度错误
原数组元素是32位的unsigned类型,占4字节,但你用rbx*8按64位步长访问,会读取错误的内存数据,甚至导致越界访问。
修复:将步长改为rbx*4,并且用mov esi, [rcx+rbx*4]加载32位元素,这样会自动零扩展到64位的rsi寄存器,符合System V ABI对32位整数参数的要求。错误3:违反寄存器调用约定
rbx属于System V ABI中的非易失性寄存器,调用者需要保存其值后才能修改。你的代码直接修改rbx,会破坏调用者上下文的rbx值,导致后续潜在错误。
修复:函数开头push rbx保存,结束前pop rbx恢复。
修复后的汇编代码
reduce: push rbx ; 保存非易失性寄存器rbx push r12 ; 保存非易失性寄存器r12 xor rbx, rbx ; 初始化计数器为0 mov r12, rdi ; 将函数指针存入r12,避免破坏rsp mov eax, esi ; 初始值存入eax(32位,自动零扩展到rax) while: cmp rbx, rdx ; 比较计数器与数组长度 jge end ; 计数器>=长度时跳转到结束 mov edi, eax ; 第一个参数:当前累计值(32位,零扩展到rdi) mov esi, [rcx + rbx*4] ; 第二个参数:数组当前元素(32位,零扩展到rsi) call r12 ; 调用传入的函数指针 inc rbx ; 计数器自增 jmp while end: pop r12 ; 恢复r12 pop rbx ; 恢复rbx ret
内容的提问来源于stack exchange,提问作者HeapUnderStop

