欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言内联汇编

程序员文章站 2024-03-23 14:07:52
...

开门见山,汇编语言和C语言混合编程可分为两大类:

  1. 单独的汇编代码文件与单独的C语言分别编译成目标文件后,一起链接成可执性文件
  2. 在C语言中嵌入汇编代码,直接编译生成可执行程序

今天主要介绍第二种
内联汇编称为inline assembly, GCC支持在C代码中直接嵌入汇编代码,所以称为GCC inline assembly.
内联汇编按格式分为两大类,一类是最简单的基本内联汇编,另一类是复杂一些的扩展内联汇编.(内联汇编中所用的汇编语言是AT&T,并不是咋们熟悉的Intel语法,GCC只支持它, AT&T是汇编语言的一种语法风格,格式.在某一处理器平台上,无论汇编代码是什么语法,其编译出来的代码是一样的,所以不要误以为AT&T是一种新的机器语言.它仅仅是表达方式不同,意思是一样的)

基本内联汇编

格式: asm[volatile] ("assembly code")

  1. gcc有个优化选项 -O,可以指定优化级别.当用-O来编译时,gcc按照自己的意图优化代码,说不定就会把自己所写的代码修改了. 关键字volatile是可选项,它告诉gcc:“不要修改我的汇编代码,请原样保留”
  2. "assembly code"是咱们所写的汇编代码,他必须位于圆括号中,而且必须用双引号引起来.这是格式要求,可以为空.规则:
    (1) 指令必须用双引号引起来,无论双引号中是一条指令或多条指令
    (2) 一对引号不能跨行,如果跨行需要在结尾用反斜杠\转义
    (3) 指令之间用 ;, 换行符\n或 换行符加制表符\n\t分隔
char *str = "hello world\n";
int count = 0;
void main()
{
asm("pusha; \
    movl $4, %eax; \
    movl $1, %ebx; \
    movl str, %ecx; \
    movl $12, %edx; \
    int $0x80; \
    mov %eax, count; \
    popa \
    ");
}

扩展内联汇编

gcc本身是个C编译器, 要让其支持汇编语言,必然牵扯以下问题:

  • 在内联汇编代码插入之前的C代码,其编译后也要被分配寄存器等资源,插入的汇编代码也要使用寄存器,这是否会造成资源冲突?
  • 汇编语言如何访问C代码中的变量
    可以看到在基本内联汇编中解决这些问题的方法,是在汇编开始用pusha将所有的寄存器压栈 ,结束后出栈popa,看起来还不错,但由用户来保证数据完整性简直就是灾难,而且,运行中有大量的压栈操作,访问内存本身就比较慢,不如在编译阶段由编译器优化,直接分配给寄存器或用寄存器缓存
    最后编译器采取的做法是它提供一个模板,让用户在模板中提出要求,其余工作由它负责.
    格式asm[volatile] ("assembly code" : output : input : clobber/modify)
  1. 寄存器约束
    a : %eax, %ax, %al
    b : %ebx, %bx, %bl
    c : %ecx, %cx, %cl
    d : %edx, %dx, %dl
    S : %esi, %si
    D : %edi, %di
//  基本内联汇编
int in_a = 1, in_b = 2, out_sum;
void main()
{
asm("pusha; \
    movl in_a, %eax; \
    movl in_b, %ebx; \
    addl %ebx, %eax; \
    movl %eax, out_sum; \
    popa");
    printf("%d\n", out_sum);
}
//扩展内联汇编
void main()
{
    int in_a = 1, in_b = 2, out_sum;
    asm("addl %%ebx, %%eax" : "=a"(out_sum) : "a"(in_a), "b"(in_b));
    printf("%d\n", out_sum);
}
  1. 内存操作数约束(Memory operand constraint, m)
    当我们不想通过寄存器中转,而是直接操作内存时,可以用"m"来约束。例如:
    asm volatile ( “lock; decl %0” : “=m” (counter) : “m” (counter));
  2. 立即数约束(不太常用)
  3. 通用约束
    为方便对操作数的引用,扩展内联汇编提供了占位符,它的作用是代表约束指定的操作数(寄存器, 内存, 立即数).
    占位符分为序号占位符和名称占位符两种序号占位符是对output和input中的操作数,按照他们从左到右出现的次序从0开始编写.引用它的格式是%0~9
    asm("addl %%ebx, %%eax" : "=a"(out_sum) : "a"(in_a), "b"(in_b));
    等价于
    asm("addl %2, %1" : "=a"(out_sum) : "a"(in_a), "m"(in_b));

可以通过gcc -S -o filename.S filename.c cat filename.S来查看寄存器的使用情况,你就会明白扩展内联汇编的具体优异之处

上一篇: string.valueof和toString的区别

下一篇: