例外场景示例
使用intel、at&t混合汇编风格
对于嵌入式汇编,gcc支持不同的汇编风格,使用“{…|…}”区分,以下例子是使用了at&t和intel两种汇编风格的嵌入式汇编代码块,当前工具仅支持at&t的汇编风格转换。
at&t和intel风格差异如下:
场景 |
at&t |
intel |
说明 |
---|---|---|---|
操作寄存器 |
%eax |
eax |
at&t使用寄存器需要带%符号 |
操作立即数 |
mov $imm,%eax |
mov ebx,imm |
at&t使用立即数需要带$符号 |
源/目的操作数顺序 |
movl %eax,%ebx |
mov ebx,eax |
at&t目的操作数在后,源操作数在前 |
内存寻址方式 |
disp(base,index,scale) |
base+index*scale+disp |
- |
1 2 3 4 5 6 7 8 9 10 |
void multiple_assembler_dialects() { int a = 10; int b = 101; __asm__( "add{l %[int_b], %[out_a] | %[out_a], %[int_b]}" :[out_a]"+r"(a) :[int_b]"r"(b): ); } |
使用goto标签
以下例子是使用goto标签的嵌入式汇编代码块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int goto_label(int a, int b) { __asm__ goto( "cmp %0, %1\n\t" "jb %l[label]\n\t" : : "r"(a), "r"(b) : : label ); return a; label: return b; } |
使用段寄存器
以下例子是使用段寄存器ds的嵌入式汇编代码块:
1 2 3 4 5 6 7 8 9 10 |
void segment_register(unsigned int *base, unsigned long index) { int output = 0; __asm__( "movl %%ds:%c4(%1, %3, 4), %0\n\t" :"=r"(output) :"r"(base), "r"(output), "r"(index), "i"(4) :"eax" ); } |
使用未赋值的物理寄存器
以下例子是使用了未赋值寄存器的嵌入式汇编代码块,寄存器eax在使用前未赋值,会导致运行结果不可知。
1 2 3 4 5 6 7 8 9 |
void unassigned_register() { int output = 0; __asm__( "movl %%eax, %0\n\t" :"=r"(output) : ); } |
使用全局符号
以下例子是使用了全局符号的嵌入式汇编代码块,全局变量Foo和外部标签out_label,对于第一个汇编代码块来说,都是全局符号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int Foo[] = {100, 101, 102}; void global_symbol() { int output = 0; __asm__( "movl 4+Foo, %0\n\t" "jmp out_label\n\t" :"=r"(output) : ); __asm__( "out_label:\n\t" ); } |
输入输出变量的位宽大于128位
以下例子是使用了大于128位变量的嵌入式汇编代码块,输出变量(result)的类型是__m256i,其位宽为256位,大于128位,由于ARM架构没有128位以上的寄存器,使用超过128位的变量,会导致汇编器传参出错,工具无法处理x86和Kunpeng两个平台之间的这种传参差异。
1 2 3 4 5 6 7 8 9 10 11 |
void variable_width(unsigned int *ymmData) { __m256i result; __asm__( "vmovups %1, %%ymm1\n\t" "vmovups %%ymm1, %0 \n\t" :"=m"(result) :"m"(ymmData) :"ymm1" ); } |
使用.byte机器码
以下例子是使用.byte机器码的嵌入式汇编代码块,请将机器码手动修改为对应的汇编指令再重新扫描转换。注意,汇编不支持迁移修改后再次扫描指的是全汇编文件(.s和.S),含嵌入式汇编的文件支持修改后重新扫描。
1 2 3 4 5 6 7 8 9 10 |
void byte_assembler_code() { int dst; asm (".byte 0xb8, 0xe5, 0x07, 0x00, 0x00\n\t" : "=a" (dst) : : ); printf("dst = %d\n", dst); } |
机器码转汇编指令可以通过在已经安装好llvm-mc的环境,执行如下llvm-mc命令完成:
1
|
echo "机器码" | llvm-mc -disassemble -show-inst --triple=x86_64 |
例如,本例中的.byte机器码使用llvm-mc转换结果如下:
其中,llvm-mc转机器码得到的汇编指令中类似eax的寄存器操作数仅含有一个“%”,而嵌入式汇编中寄存器需要两个“%”,因此本例中的机器码".byte 0xb8, 0xe5, 0x07, 0x00, 0x00"最终修改成的汇编指令是"movl $2021, %%eax"。