x86下AT&T linux汇编入门
linux汇编AT&T编程目录:·介绍·intel和AT&T语法 ·前缀 ·操作数方向 ·内存操作数 ·后缀·系统调用 ·少于6个参数的系统调用 ·多于5个参数的系统调用 ·socket系统调用·命令行参数·GCC内联ASM·
linux汇编AT&T编程
目录:
·介绍
·intel和AT&T语法
·前缀
·操作数方向
·内存操作数
·后缀
·系统调用
·少于6个参数的系统调用
·多于5个参数的系统调用
·socket系统调用
·命令行参数
·GCC内联ASM
·编译
·参考
介绍
这篇文章主要是一篇linux汇编语言编程入门教程,包含了Intel与AT&T语法格式的比较以及汇编语言中如何使用系统调用和介绍gcc中如何使用内联汇编
Intel和AT&T语法
Intel和AT&T在语法格式上有很大的不同,主要表现在以下几个方面:
前缀
Intel语法
move ax,1 mov ebx,0ffh int 80h | AT&T语法
movl $1,%eax movl $0xff,%ebx int 80h |
操作数方向
Intel语法中操作数方向与AT&T是相反的,在Intel语法中第一个操作数是目的操作数,第二个操作数是源操作数,但AT&T语法格式中,第一个操作数是源操作数,第二个操作数是目的操作数,AT&T语法的优点就是比较人性化,符号我们从左到右阅读的习惯逻辑。
Intel语法
instr dest,source mov eax,[ecx] | AT&T语法
instr source,dest movl (%ecx),%eax |
内存操作数
从上面表格中也能看出两种语法格式在使用内存操作数时也有不同,Intel语法格式用’[‘和’]’表示引用一个基地址,而AT&T语法中使用’(‘和’)’来引用一个基地址
Intel语法
move eax,[ebx] mov eax,[ebx+3] | AT&T语法
movl (%ebx),%eax movl 3(%ebx),%ebx |
另外特别是在进行对内存寻址的时候,AT&T语法可能就不如intel语法格式那么直观,比如intel语法采用segreg:[base+index*scale+disp]而AT&T采用segreg:disp(base,index,scale)来表示变址寻址。
index/scale/disp/segreg都是简单的位于左边的选项,如果没有指定scale,而指定了index,那么scale默认为1,segreg选项依赖于应用是运行在实模式还是保护模式,并且它只用于实模式中。另外,在Intel语法格式中立即数不需要加’$’,而在AT&T语法中加’$’前缀来表示立即数。
Intel语法
instr foo,segreg:[base+index*scale+disp] mov eax,[ebx+20h] add eax,[ebx+ecx*2h] lea eax,[ebx+ecx] sub eax,[ebx+ecx*4h-20h] | AT&T语法
instr segreg:disp(base,index,scale),foo movl 0x20(%ebx),%eax addl (%ebx,%ecx,0x2),%eax leal (%ebx,%ecx),%eax subl -0x20(%ebx,%ecx,0x4),%eax |
后缀
也许你已经注意到,AT&T语法助记符中有一个后缀标记,如’l’,后缀的特征表示操作数大小,如’l’表示long类型,’w’表示一个字长,’b’表示一个字节长,intel语法也有类似的指令用在内存操作数中,例如byte ptr,word ptr,dword ptr,’dword’表示’’long’型,这有点类似于C语言中的强制转换,但也并不一定非这样不可,因为寄存器的大小已经使用在假定的数据类型中了。
Intel语法
move al,bl mov ax,bx mov eax,ebx mov eax,dword ptr [ebx] | AT&T语法
movb %bl,%al movw %bx,%ax movl %ebx,%eax movl (%ebx),%eax |
系统调用
系统调用的所有函数原型在manual手册的第二段,文件/usr/man/man2中,也可以在/usr/include/sys/syscall.h中找到,也可以通过这个链接访问,所有的系统调用函数都通过linux软中断服务(int $0x80)来执行的。
这个我前一篇文章中已经提到了,linux下系统调用的设计都会遵循这个原则,只是在AT&T语法中多了一个前缀符而已,这里我就再讲了,下面是参数小于6个和大于5个时的两段代码:
syscall < 6 args
$ cat write.s
.include "defines.h"
.data
hello:
.string "hello world/n"
.globl main
main:
movl $SYS_write,%eax
movl $STDOUT,%ebx
movl $hello,%ecx
movl $12,%edx
int $0x80
ret
$
系统调用参数大于5个
mmap()参数在栈中的位置:
%esp | %esp+4 | %esp+8 | %esp+12 | %esp+16 | %esp+20 |
00000000 | filele | 00000001 | 0000000 | fd | 00000000 |
$ cat mmap.s
.include "defines.h"
.data
file:
.string "mmap.s"
fd:
.long 0
filelen:
.long 0
mappedptr:
.long 0
.globl main
main:
push %ebp
movl %esp,%ebp
subl $24,%esp
// open($file, $O_RDONLY);
movl $fd,%ebx // save fd
movl %eax,(%ebx)
// lseek($fd,0,$SEEK_END);
movl $filelen,%ebx // save file length
movl %eax,(%ebx)
xorl %edx,%edx
// mmap(NULL,$filelen,PROT_READ,MAP_SHARED,$fd,0);
movl %edx,(%esp)
movl %eax,4(%esp) // file length still in %eax
movl $PROT_READ,8(%esp)
movl $MAP_SHARED,12(%esp)
movl $fd,%ebx // load file descriptor
movl (%ebx),%eax
movl %eax,16(%esp)
movl %edx,20(%esp)
movl $SYS_mmap,%eax
movl %esp,%ebx
int $0x80
movl $mappedptr,%ebx // save ptr
movl %eax,(%ebx)
// write($stdout, $mappedptr, $filelen);
// munmap($mappedptr, $filelen);
// close($fd);
movl %ebp,%esp
popl %ebp
ret
$
socket系统调用
socket系统调用仅仅使用一个系统调用号:SYS_socket,放在%eax中,socket函数通过一个字函数号进行区分,位于/usr/include/linux/net.h中,子函数号存在%ebx中,指向系统调用参数的指针存在%ecx中,同样通过int $0x80中断来执行。
$ cat socket.s
.include "defines.h"
.globl _start
_start:
pushl %ebp
movl %esp,%ebp
sub $12,%esp
// socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
movl $AF_INET,(%esp)
movl $SOCK_STREAM,4(%esp)
movl $IPPROTO_TCP,8(%esp)
movl $SYS_socketcall,%eax
movl $SYS_socketcall_socket,%ebx
movl %esp,%ecx
int $0x80
movl $SYS_exit,%eax
xorl %ebx,%ebx
int $0x80
movl %ebp,%esp
popl %ebp
ret
命令行参数
见上篇文章NASM汇编入门
gcc内联汇编
下面是一篇关于这个话题讲得很仔细的文章:
编译
编译汇编语言跟编译C差不多,下面是两种不同代码的相应编译方式:
$ cat write.s .data hw: .string "hello world/n" .text .globl main main: movl $SYS_write,%eax movl $1,%ebx movl $hw,%ecx movl $12,%edx int $0x80 movl $SYS_exit,%eax xorl %ebx,%ebx int $0x80 ret $ gcc -o write write.s $ wc -c ./write 4790 ./write $ strip ./write $ wc -c ./write 2556 ./write
|
$ cat write.s .data hw: .string "hello world/n" .text .globl _start _start: movl $SYS_write,%eax movl $1,%ebx movl $hw,%ecx movl $12,%edx int $0x80 movl $SYS_exit,%eax xorl %ebx,%ebx int $0x80
$ gcc -c write.s $ ld -s -o write write.o $ wc -c ./write 408 ./write
|
-s开关选项实际是执行strip,去除一些无关的信息,使得ELF文件更小
参考链接
更多推荐
所有评论(0)