linux下c语言嵌入汇编学习小结

gcc内嵌汇编
主要来源: http://nuieee.fe.up.pt/~ee97034/la/linasm.html
Using Assembly Language in Linux.
Intel和AT&T汇编语法差异:
1。前缀:
Intel汇编寄存器和立即数无需前缀。后者寄存器前缀为%,立即数前缀为$。
eg:
Intex Syntax
mov eax,1
mov ebx,0ffh
int 80h

AT&T Syntax
movl $1,%eax
movl $0xff,%ebx
int   $0×80
2。二者对操作数处理方向不同:
eg:
Intel 汇编语法:
instr dest,source
mov eax,[ecx]

AT&T 汇编语法:
instr   source,dest
movl (%ecx),%eax
3。内存变量语法差异:
Intel语法使用中括号[],后者使用小括号()
eg:
Intex Syntax
mov eax,[ebx]
mov eax,[ebx+3]

AT&T Syntax
movl (%ebx),%eax
movl 3(%ebx),%eax
4。后缀:
AT&T汇编指令有后缀,以表明数据类型(8位、16位等);Intel则根据寄存器自动识别。
eg:
Intel Syntax
mov al,bl
mov ax,bx
mov eax,ebx
mov eax, dword ptr [ebx]

AT&T Syntax
movb %bl,%al
movw %bx,%ax
movl %ebx,%eax
movl (%ebx),%eax

5。
Intel:segreg:[base+index*scale+disp]
AT&T: %segreg:disp(base,index,scale). 
例子:
1 #include <stdio.h>
2
3 int main(void) {
4      long eax=4;
5      long ebx=2;
6
7      __asm__ __volatile__ (“addl %1, %0″
8              : ”=b”((long)ebx)
9              : ”a”((long)eax), ”b”((long)ebx)
10              : ”1″
11              );
12
13      printf(“ebx=%d
“, ebx);
14      return 0;
15 }
输出结果:ebx=6
linux下c语言内嵌汇编格式:
__asm__(“<asm routine>” : output : input : modify);
output, input, modify都是引号括起来的字符串,他们直接用冒号分隔。
每个都可以有多项,项之间用逗号分隔,最多总共不能超过10个。
output要用“=”开头

寄存器缩写约定
Abbrev Register
a   %eax/%ax/%al
b   %ebx/%bx/%bl
c   %ecx/%cx/%cl
d   %edx/%dx/%dl
S   %esi/%si
D   %edi/%di
m   memory
q 由编译器在a、b、c、d中任意选择
寄存器还可以使用数字形式的缩写,0~9 (%0-%9),所以会有最多10个项的限制。
系统调用:
1。系统调用号存在eax中
2。系统调用参数要少于6个,分别存放在ebx, ecx, edx, esi, edi
3。返回值存在eax中
4。参数超过5个,用ebx指向存放参数的内存。如果压入堆栈,要注意参数顺序反过来。
汇编文件入口:
如果使用gcc编译,入口为main;如果用as和ld编译连接,入口使用_start,可以用ld的-e选项指定程序入口:ld -e main -o hello hello.o。
汇编程序运行时出现segmentation fault:
程序运行完时,处理器试图去执行下一个内存单元的指令,内存中没有指令或者为非法指令,硬件或者os包含机制就会发现
这个异常而产生段包含错误。解决方法,加上退出程序的系统调用就可以了。
eg:
1 #.include ”defines.h”
2 .data
3 hello:
4 .string ”hello world

5
6 .globl   main
7 main:
8 #write
9 movl     $4,%eax
10 #stdout
11 movl     $1,%ebx
12 #content
13 movl     $hello,%ecx
14 #length of string
15 movl     $12,%edx
16 #system call
17 int      $0×80
18
19 #exit
20 movl     $1,%eax
21 int      $0×80
22
23 ret

 

 

最近要改个C语言算法的关键部分用汇编语言实现,Linux里嵌入汇编基本使用AT&T汇编,比如Linux系统的启动部分用的就是AT&T汇编 。以前学过AT&T汇编,但学过一段时间就忘了,但对Intel汇编基础比较熟悉,两者使用方法基本相似,所以对着Intel汇编,花点时间看AT&T汇编也就容易了。
下面看一下两者的区别,然后给出Linux语言中嵌入AT&T汇编的具体的例子。
一、AT&T汇编和Intel汇编主要区别
1,两者源和目的操作数次序相反。 AT&T的源和目的是从左到右,并且其寄存器前要加“%”;Intel的是右到左,不需要加”%”。
例如:AT&T: movl %ecx, %eax (ecx为源操作数,eax为目的操作数)
Intel: mov dx, bx    (bx为源操作数,dx为目的操作数
2,AT&T立即操作数前需要加”$”;Intel的不用
例如:AT&T:movl $2, %eax
Intel:mov ax, 2
3,AT&T中内存操作的长度由操作码最后一个字符来确定。”b”,”w”,”l”分别表示内存引用为1字节8位,2字节16位,4字节32位。
Intel使用操作前缀byte ptr, word ptr, dword ptr
例如:AT&T:movl %ecx, %eax
Intel:mov al, byte ptr ttt

这三点是最重要的区别,除此之外还有跳转/调用时不太一样。

二、嵌入汇编基本格式

asm(“汇编语句”
“输出寄存器”
“输入寄存器”
“会被修改的寄存器”//告诉编译器在执行这条__asm__语句时这里指定的寄存器要被改写,所以在此期间不要用这里的寄存器保存其它值。
)

//asm.c
#include <stdio.h>

unsigned int LeftShift(unsigned int uiNumber, unsigned char iBits)
{
register unsigned int _res;
__asm__(“rol %1, %%eax;”
:”=a” (_res)
:”c” (iBits),”0″(uiNumber)
);

return _res;
}

int main(void)
{
unsigned int ret = 0;

ret = LeftShift(4, 2);
printf(“1,ret:%u
“, ret);

return 0;
}

[root@xxx asm_study]# gcc asm.c -o app
[root@xxx asm_study]# ./app
1,ret:16

以上给出一个循环左移的例子,不关注循环左移指信rol本身,只关注AT&T汇编用法。
首先定义了个寄存器变量_res,用其保存返回值。
因为AT&T汇编使用寄存器,其前面需要带“%”,而在c语言中“%”是个特殊格式字符,所以需要两个百分号“%%”才最终表示一个“%”,这个和转义字符有点类似。
:”=a”(_res)表示用寄存器eax的值输出给_res,用%0表示此寄存器,
:”c” (iBits),”0″(uiNumber),c表示输入寄器ecx,0表示使用上面的输出寄存器eax,所以ecx和eax分别保存iBits和uiNumber变量值。指令中的%1指寄存器ecx,所以其编号规则是从“输出寄器”开始从左到右,从上到下进行的。
可以发现rol %1,%%eax其实可以直接写成:rol %1, %0

三、C语言版循环左移
给出一个C语言版循环左移代码,有兴趣的朋友可以和上面的汇编版进行对比。
//ori.c
unsigned int LeftShift2(unsigned int uiNumber, unsigned char iBits)
{
int i=0;
unsigned int iret=uiNumber;
unsigned int iR=0×01;

for(i=0;i<iBits;i++)
{
iR=iret&0×80000000;
iret=iret<<1;
iR=iR>>31;
iret=iret|iR;
}

return iret;
}
int main(void)
{
unsigned int ret = 0;

ret = LeftShift2(4,2);
printf(“2,ret:%u
“, ret);

return 0;
}
[root@xxx asm_study]# gcc ori.c -o app
[root@xxx asm_study]# ./app
2,ret:16

四、宏定义版汇编
#define LeftShift(uiNumber, iBits)
({
register unsigned int __res;
__asm__(“rol %%cl, %%eax;”
:”=a”(__res)
:”c” (iBits),”a”(uiNumber));
__res;})
和普通的汇编区别是每一行后面都要加”",返回值写最后,如上面最后的__res。

五、c程序转变为汇编程
1,把*.c程序转变为AT&T格式汇编*.s
[root@xxx asm_study]# gcc -S asm.c -o asm.s
[root@xxx asm_study]# ls -al asm.s
-rw-r–r– 1 root root 1387 06-30 10:41 asm.s

2,把*.c程序转变为Intel格式汇编*.s
[root@xxx asm_study]# gcc -masm=intel test.c -o test.s
当然,要想把c程序转为Intel汇编时,其中不能包含AT&T格式的汇编,否则无法转。
作者:rosetta

http://www.2cto.com/kf/201207/138509.html

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>