C调用汇编

一、汇编语言的两种语法格式
 
Intel格式: 大多数DOS、Windows环境采用
AT&T格式:Unix和Linux系统采用(GCC编译器也采用AT&T)
 
详见附件《Linux 汇编器:对比 GAS 和 NASM》 http://www.ibm.com/developerworks/cn/linux/l-gas-nasm.html#sidebar
 
二、C语言调用汇编程序
 
2.1 扩展内联汇编——C语言中嵌入汇编程序
 
基本内联汇编  不能 让汇编程序与C程序交互,形如:  __asm__("pushl %eax");
 
扩展内联汇编  可将C语言表达式的值作为参数输入,并可将汇编处理的结果返回,语法格式为:    
                            __asm__    __volatile__( " 汇编指令" :   输出 :   输入 :   修改)
    注:扩展内联汇编的4个部分都不是必须的,如果省略后面3个部分则变成基本内联汇编。下面均介绍扩展内联汇编。
 
 
例1——基本语法
#include <stdio.h>
int main(){
 int i, j;
 scanf("%d%d", &i, &j);
 printf("i=%d, j=%d\n", i, j);
 
 //set j to i
 // __asm__ __volatile__(
 // "movl 8(%%esp), %0\n"
 // :"=a"(i)
 // );
 
 __asm__ __volatile__(
 "movl %1, %%eax; incl %%eax; movl %%eax, %0"
 :"=a"(i)
 :"r"(j)
 );
 printf("i=%d, j=%d\n", i, j);
 getch();
 return 0;
}
 
注释:
A. __asm__ 关键字定义一个内联汇编表达式,括号内是一条/多条汇编指令序列,多条则用"\n", "\n\t"或";"隔开
B. __volatile__关键字是一个可选项,如果包含了该关键字,则告诉编译器,对后面的内联汇编代码不能优化。通常,为了不让GCC的优化影响了内联汇编代码,最好使用该关键字
C. 本例中在"输入"和"输出"部分,占位符%0,%1按顺序分别表示C语言中的变量i和变量j
D. 在使用内联汇编机制时,%数字 表示 操作数占位符, %%eax 表示 寄存器(此时需要使用2个%)
E. 实际上,"汇编指令"、"输入"、"输出"都生成了汇编代码
 
例2——利用(扩展)内联汇编,得到寄存器内容
#include <stdio.h>
 
unsigned int getesp(){
 unsigned int i;
 __asm__ __volatile__(
 "movl %%esp, %0;"
 : "=r"(i)
 );
 return i;
}
 
unsigned int getebp(){
 unsigned int i;
 __asm__ __volatile__(
 "movl %%ebp, %0;"
 : "=r"(i)
 );
 return i;
}
 
int main(){
 printf("esp=0x%x\n", getesp());
 printf("ebp=0x%x\n", getebp());
 //getch();
 return 0;
}
 
(1) " 汇编指令" :    指令中的占位符表示的操作数,总被作为long型(4字节,32bit),根据指令决定使用操作数的哪一部分。
                     % b1    使用低字节    % h1    使用高字节
                     % w1    使用低字
 
(2)  输出 :   格式为   "=限制字符"(C表达式), "=限制字符"(C表达式), ... ...
                限制字符可以用一个字母对寄存器进行约束,各字符的含义如下:
                    r : 使用一个通用寄存器,由GCC在通用寄存器中选取一个使用;
                    q : 同r
                    a : 使用寄存器%eax, %ax, %al
                    b : 使用寄存器%ebx, %bx, %bl
                    c : 使用寄存器%ecx, %cx, %cl
                    d : 使用寄存器%edx, %dx, %dl
                    D : 使用寄存器%edi, %di
                    S : 使用寄存器%esi, %si
                    f : 使用浮点寄存器
                    t : 使用第一个浮点寄存器
                    u : 使用第二个浮点寄存器
                    m : 使用系统支持的任何一种内存方式,不借助寄存器
                    g : 可以使用通用寄存器、内存、立即数等任何一种方式           
 
(3)  输入 
 
(4)  修改 : 有空再看
 
 
 
 
2.2 单独编写汇编程序,编译为目标文件,供C语言调用
 
A. 理解原理:熟悉C编译器的 调用约定
 
C程序:从右向左压参数,压入放回地址;执行时,从栈顶取参数;结束时,函数返回值放入寄存器中返回(通常为%eax)
     注:esp是栈顶指针,为了方便在函数中均采用"栈顶+offset"来访问,但是却不直接使用esp,而是把esp赋予ebp来使用。
 
 
 
对应汇编程序:从中可以提炼出汇编函数的大致框架
 
 
B. 理解原理后自己都知道C语言是怎么调用汇编的.o程序的了
    按照上面框架,造一个汇编写的加法程序add.s,用 gcc -c add.s就可以生成add.o
.globl add
 .type add, @function
add:
 pushl %ebp
 movl %esp, %ebp
 movl 12(%ebp), %eax
 addl 8(%ebp), %eax
 popl %ebp
 ret
 .size add, .-add
 .section .rodata
 
    在C程序中只需要生命函数原型即可,保证和汇编声明的函数名称一致
#include <stdio.h>
 
int add(int x, int y);
 
int main(){
 int i=1, j=2,n;
 n = add(i, j);
 printf("n=%d\n", n);
 
 return 0;
}
 
    好,这样就可以生成最终的可执行main.o了:   gcc -o main.o add.o main.c
 
 
 
参考文档:
《C语言开发技术详解》(戴建华 等编著,电子工业出版社)
 


 

你可能感兴趣的:(汇编)