GCC内联汇编cmpxchgl示例中EAX加载_old值的来源问题
GCC内联汇编
cmpxchg实现中EAX加载逻辑与"0"约束原理 操作数的编号与约束规则
GCC内联汇编的操作数按照「输出操作数在前、输入操作数在后」的顺序,从0开始连续编号。这段代码里的操作数对应关系如下:
- 编号0:输出操作数,约束为
"=a",绑定C变量__ret
x86架构下a是固定寄存器约束,直接指定该操作数必须存放在EAX寄存器中,=标记表示这是只写的输出操作数。 - 编号1:输出操作数,约束为
"+m",绑定内存位置*__ptrm代表内存操作数,+标记表示该操作数支持读写。 - 编号2:输入操作数,约束为
"r",绑定C变量_newr代表通用寄存器约束,编译器可自主选择任意空闲通用寄存器存放_new的值。 - 输入列表里的
"0" (_old)是匹配约束:数字0表示这个输入操作数必须和编号为0的操作数占用完全相同的存储位置——也就是必须放在EAX寄存器中。
EAX加载指令的生成来源
你在汇编输出里看到的mov eax,DWORD PTR [rbp-0x1c],是GCC根据操作数约束自动生成的前置准备指令,不需要在内联汇编模板里手写:
- 编译器识别到输入操作数
_old被"0"约束绑定到EAX寄存器,在执行lock; cmpxchgl %2,%1指令之前,必须保证EAX中已经存好了_old的比较预期值。 - 如果
_old当前不在EAX里(比如示例中是存在栈上rbp-0x1c的位置),编译器就会自动生成对应的mov加载指令,把_old搬运到EAX中,满足指令的执行要求。 cmpxchgl执行完成后,EAX中会留存指令的执行结果(比较交换成功时为原预期值,失败时为内存中读取到的实际值),刚好被绑定到EAX的输出操作数__ret接收,用来判断交换是否成功。
写GCC内联汇编时不要在汇编模板里手写
mov eax, xxx这类指令给固定寄存器传参,只需要通过约束描述清楚每个操作数的存放要求即可,编译器会自动处理所有寄存器加载、保存、恢复的逻辑。手写固定寄存器的mov指令很容易破坏编译器的寄存器分配假设,引发隐蔽的bug。
内容的提问来源于stack exchange,提问作者Anthony Alba
相关产品推荐
相关产品推荐

