您的位置:首页 > 运维架构 > Linux

Linux 环境下汇编语言

2007-10-16 20:49 211 查看
Linux 汇编
Linux 环境下汇编语言
By Phillip
翻译:Yijian
介绍:
Intel 和 AT&T的汇编语法格式
在Intel的汇编语法格式里寄存器前缀,而 AT&T汇编语法格式寄存器则有 ‘%’ 前缀,立即数前面要有前缀’$’
在Intel的汇编语法格式里十六进制和字节立即数风别用加’h’和’b’后缀。并且如果十六进制的第一个数字是字母的话,这个值就需要加一个前缀‘0’。
例子:
Intel格式
mov eax,1
mov ebx,0ffh
int 80h AT&T格式
movl $1,%eax
movl $0xff,%ebx
int $0x80
操作数发方向
AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边,我们通常从左到右阅读,AT&T汇编格式的用起来比较自然,这可能是它的优点
例子:
Intex 格式
指令 dest,source
mov eax,[ecx] AT&T 格式
指令 source,dest
movl (%ecx),%eax
内存操作
内存操作Intel和AT&T也是不一样。在Intel汇编格式基寄存器都用’[‘和’]’关闭,而AT&T格式用’(‘和’)’
例子:
Intex 格式
mov eax,[ebx]
mov eax,[ebx+3] AT&T 格式
movl (%ebx),%eax
movl 3(%ebx),%eax
系统调用

1 参数小于6的系统调用
对于所有的系统调用,寄存器 eax 中存放的是系统调用的功能号.如果系统调用的参数小于6个。按顺序存放在ebx,ecx,edx,esi,edi五个寄存器里。返回值存储在eax里
系统调用的功能号在/usr/include/sys/syscall.h下面。
例子:
$ 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

2.系统调用的参数大与5
功能号仍然放在eax.但是参数放在连续的内存里了,并且指向第一个参数的指针的值放到ebx寄存器里。
如果你用堆栈的话,参数必须从最后一个到第一个一次push到堆栈里。堆栈的指针应该放到ebx里。
例如:
(mmap being the example syscall). Using mmap() in C:
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

#define STDOUT 1

void main(void) {
char file[]="mmap.s";
char *mappedptr;
int fd,filelen;

fd=fopen(file, O_RDONLY);
filelen=lseek(fd,0,SEEK_END);
mappedptr=mmap(NULL,filelen,PROT_READ,MAP_SHARED,fd,0);
write(STDOUT, mappedptr, filelen);
munmap(mappedptr, filelen);
close(fd);
}
Arrangement of mmap() args in memory:
%esp %esp+4 %esp+8 %esp+12 %esp+16 %esp+20
00000000 filelen 00000001 00000001 fd 00000000
ASM Equivalent:
$ 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系统调用只有一个功能号放到eax里。Socket函数次功能号在/usr/include/linux/net.h里,并且放到ebx里。指向参数的指针放到ecx里。Socket的系统调用仍然是通过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
$
命令行参数
在 Linux 操作系统中,当一个可执行程序通过命令行启动时,其所需的参数将被保存到栈中:首先是 argc,然后是指向各个命令行参数的指针数组 argv,最后是指向环境变量的指针数据 envp。在编写汇编语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如何在汇编代码中进行命令行参数的处理:
例如:
/* args.s */
.text
.globl _start
_start:
popl %ecx // argc
lewp:
popl %ecx // argv
test %ecx,%ecx
jz exit

movl %ecx,%ebx
xorl %edx,%edx
strlen:
movb (%ebx),%al
inc %edx
inc %ebx
test %al,%al
jnz strlen
movb $10,-1(%ebx)

// write(1,argv[i],strlen(argv[i]));
movl $SYS_write,%eax
movl $STDOUT,%ebx
int $0x80

jmp lewp
exit:
movl $SYS_exit,%eax
xorl %ebx,%ebx
int $0x80

ret

GCC 嵌套汇编
GCC基本的嵌套汇编非常明了,基本的格式如下:
__asm__("movl %esp,%eax");
或者:
__asm__("
movl $1,%eax // SYS_exit
xor %ebx,%ebx
int $0x80
");
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: