NASM编写修改C指针指向内存的汇编函数运行失败,该如何正确实现?
程序崩溃的核心原因
你写的内存写入逻辑本身没有问题,程序崩溃的根源是违反了32位Linux System V的函数调用约定,和内存访问方式无关:
- 按照32位Linux的调用规则,寄存器分为两类:
- 被调用者保存寄存器:包括
ebx、esi、edi、ebp,被调用的汇编函数如果要修改这些寄存器,必须在函数入口把原值压栈保存,返回前恢复原值,不然就会破坏C代码的运行上下文,直接触发段错误。你原来的代码直接修改了ebx,既没保存也没恢复,这就是崩溃的直接原因。 - 调用者保存寄存器:包括
eax、ecx、edx,被调用函数可以随意修改这些寄存器,不需要做保存恢复操作,因为C编译端的代码会默认这些寄存器在函数调用后值可能被覆盖。
- 被调用者保存寄存器:包括
正确的实现方式
方案1:使用无需保存的易失寄存器(代码最简洁)
直接换用调用者保存的寄存器做临时存储,完全不需要额外栈操作,代码逻辑和你原来的设计完全一致,但不会破坏程序状态:
section .text global _mptr _mptr: ; 取两个参数,放到易失寄存器edx、ecx里 mov edx, [esp + 4] ; edx存第一个参数:ptr mov ecx, [esp + 8] ; ecx存第二个参数:val ; 把val写入ptr指向的内存 mov [edx], ecx ; 返回值放到eax,就是原来的ptr mov eax, edx ret
这个版本编译运行后,就能正确把225写入i的内存地址,输出符合预期。
方案2:如果一定要用ebx这类非易失寄存器
必须严格按照调用约定保存、恢复寄存器值:
section .text global _mptr _mptr: ; 先保存ebx原值到栈上 push ebx ; 因为前面push了4字节,栈偏移整体+4,原参数位置整体后移 mov ebx, [esp + 8] ; 原第一个参数ptr mov ecx, [esp + 12] ; 原第二个参数val mov [ebx], ecx mov eax, [esp + 8] ; 返回值ptr ; 恢复ebx原值 pop ebx ret
常见误区澄清
很多新手会误以为是自己的指针访问语法错了——x86下写指针指向内存的标准操作就是:先把指针的32位地址值加载到通用寄存器,再通过mov [寄存器], 要写入的值的格式完成写入,你原来的代码用ebx存指针、往[ebx]写值的语法本身是完全正确的,只要指针指向的是合法可写的用户空间内存,写入操作就不会出错。
内容的提问来源于stack exchange,提问作者Ivan Venkov3
相关产品推荐
相关产品推荐

