一、汇编语言的两种语法格式
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语言开发技术详解》(戴建华 等编著,电子工业出版社)