Clang生成的内联汇编代码是否存在错误?
内联汇编在GCC与Clang下的行为差异及问题解决
我在Intel Mac笔记本上,用自行编译的GNU GCC(命令gnucc)和macOS内置Clang编译同一段包含内联汇编的C代码:
- GCC编译后的程序输出预期值2
- Clang编译后的程序输出错误的大数值140701838959608
在Compiler Explorer上测试也得到相同差异结果。反汇编Clang生成的二进制文件后发现,本应执行movq %rcx, (%rax)的指令被替换为movq %rax, (%rax),需要确认是Clang的问题还是内联汇编写法有误。
测试代码
#include "stdio.h" typedef struct num { unsigned long long x; } num; int main(int argc, char **argv) { struct num anum; anum.x = 0; __asm__("movq %%rax, %0\n" : "=m" (anum.x) : "rax"(2)); printf("%llu\n",anum.x); }
Clang生成的二进制反汇编片段
clang-test: file format mach-o 64-bit x86-64 Disassembly of section __TEXT,__text: 0000000100003f60 <_main>: 100003f60: 55 pushq %rbp 100003f61: 48 89 e5 movq %rsp, %rbp 100003f64: 48 83 ec 20 subq $32, %rsp 100003f68: 89 7d fc movl %edi, -4(%rbp) 100003f6b: 48 89 75 f0 movq %rsi, -16(%rbp) 100003f6f: 48 c7 45 e8 00 00 00 00 movq $0, -24(%rbp) 100003f77: 48 8d 45 e8 leaq -24(%rbp), %rax 100003f7b: b9 02 00 00 00 movl $2, %ecx 100003f80: 48 89 00 movq %rax, (%rax) 100003f83: 48 8b 75 e8 movq -24(%rbp), %rsi 100003f87: 48 8d 3d 16 00 00 00 leaq 22(%rip), %rdi ## 0x100003fa4 <_printf+0x100003fa4> 100003f8e: b0 00 movb $0, %al 100003f90: e8 09 00 00 00 callq 0x100003f9e <_printf+0x100003f9e> 100003f95: 31 c0 xorl %eax, %eax 100003f97: 48 83 c4 20 addq $32, %rsp 100003f9b: 5d popq %rbp 100003f9c: c3 retq Disassembly of section __TEXT,__stubs: 0000000100003f9e <__stubs>: 100003f9e: ff 25 5c 00 00 00 jmpq *92(%rip) ## 0x100004000 <_printf+0x100004000>
问题原因及解决方法
这是内联汇编约束写法错误导致的问题:
- 内联汇编的输入约束部分,不能直接写寄存器名
"rax",而应该使用编译器规定的约束符。比如要指定rax寄存器,需用"a"(对应rax/eax/ax/al系列寄存器);如果允许编译器自由选择通用寄存器,用"r"即可。 - 原代码中的
"rax"(2)是无效约束,Clang无法正确解析,因此没有将值2放入rax,反而把anum.x的内存地址加载到rax中,执行movq %%rax, %0后,相当于把anum.x的地址写入了自身,最终输出的就是这个地址值(即你看到的大数值)。GCC对这种错误写法做了兼容处理,碰巧将2放入rax,才得到正确结果。
修正后的代码(指定rax寄存器)
#include "stdio.h" typedef struct num { unsigned long long x; } num; int main(int argc, char **argv) { struct num anum; anum.x = 0; __asm__("movq %%rax, %0\n" : "=m" (anum.x) : "a"(2)); // 使用"a"约束指定rax寄存器 printf("%llu\n",anum.x); }
更通用的写法(让编译器分配寄存器)
#include "stdio.h" typedef struct num { unsigned long long x; } num; int main(int argc, char **argv) { struct num anum; anum.x = 0; __asm__("movq %1, %0\n" : "=m" (anum.x) : "r"(2)); // 编译器自动选择寄存器,用%1引用输入值 printf("%llu\n",anum.x); }
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

