Linux AT&T汇编 系统调用

.data  #数据段声明
msg:  
    .ascii "Hello world, hello AT&T asm!\n"  #要输出的字符串
    len = . - msg  #字符串长度
  
.text  #代码段声明
.global _start  #指定入口函数
  
_start:  
    movl    $len,   %edx    # string length 参数三: 显示的字符数  
    movl    $msg,   %ecx    # string address 参数二 : 缓冲区指针  
    movl    $1, %ebx    # file descriptor 参数一:文件描述符( stdout)  
    movl    $4, %eax    # system call 系统调用号,sys_write  
    int $0x80       # 调用内核功能  
  
    movl    $0, %ebx    # 参数一:退出代码  传给sys_exit的参数  
    movl    $1, %eax    # 系统调用号,sys_exit
    int $0x80       # 调用内核功能



System calls are APIs for the interface between user space and kernel space. We have already used the system calls sys_write and sys_exit for writing into the screen and exiting from the program, respectively.

Linux System Calls

You can make use of Linux system calls in your assembly programs. You need to take the following steps for using Linux system calls in your program:

  • Put the system call number in the EAX register.

  • Store the arguments to the system call in the registers EBX, ECX, etc.

  • Call the relevant interrupt (80h).

  • The result is usually returned in the EAX register.

There are six registers that store the arguments of the system call used. These are the EBX, ECX, EDX, ESI, EDI, and EBP. These registers take the consecutive arguments, starting with the EBX register. If there are more than six arguments, then the memory location of the first argument is stored in the EBX register.

The following code snippet shows the use of the system call sys_exit:

mov	eax,1		; system call number (sys_exit)
int	0x80		; call kernel

The following code snippet shows the use of the system call sys_write:

mov	edx,4		; message length
mov	ecx,msg		; message to write
mov	ebx,1		; file descriptor (stdout)
mov	eax,4		; system call number (sys_write)
int	0x80		; call kernel

All the syscalls are listed in /usr/include/asm/unistd.h, together with their numbers (the value to put in EAX before you call int 80h).

The following table shows some of the system calls used in this tutorial:

%eax Name %ebx %ecx %edx %esx %edi
1 sys_exit int - - - -
2 sys_fork struct pt_regs - - - -
3 sys_read unsigned int char * size_t - -
4 sys_write unsigned int const char * size_t - -
5 sys_open const char * int int - -
6 sys_close unsigned int - - - -

http://www.tutorialspoint.com/assembly_programming/assembly_system_calls.htm

http://asm.sourceforge.net/syscall.html




        由于我喜欢一些底层的东西,不由得想弄一下AT&T格式的汇编。linux 内核中的启动部分就有用该汇编代码完成的。
程序员学习一门新的语言,一般的思维就是现实现一下输出“hello world!”。下面就拿这个下的程序来说明ATT汇编的特点吧。
    
汇编的优点:
1,能够直接访问与硬件相关的存储器或 I/O 端口;
2,能够不受编译器的限制,对生成的二进制代码进行完全的控制;
3,能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;
4,能够根据特定的应用对代码做最佳的优化,提高运行速度;
5,能够最大限度地发挥硬件的功能。

汇编的缺点。
1,编写的代码非常难懂,不好维护;
2,很容易产生 bug,难于调试;
3,只能针对特定的体系结构和处理器进行优化;
4,开发效率很低,时间长且单调。

Linux 下用汇编语言编写的代码具有两种不同的形式。
1,第一种是完全的汇编代码,指的是整个程序全部用汇编语言编写。
2,第二种是内嵌的汇编代码,指的是可以嵌入到 C 语言程序中的汇编代码片段。

一,ATT汇编程序

# hello.s  ---->  intel汇编的注释用的; 而ATT用的# # display a string "Hello, world." .section .rodata msg: .ascii "Hello, world.\n" .section .text .globl _start _start:

  movl $2, %eax  #调用fork系统调用 int $0x80

   movl $4, %eax # system call  系统调用号(sys_write) movl $1, %ebx # file descriptor 参数一:文件描述符(stdout) movl $msg, %ecx # string address  参数二:要显示的字符串 movl $14, %edx # string length  参数三:字符串长度 int $0x80          # 调用内核功能 movl $1, %eax     # 系统调用号(sys_exit) movl $0, %ebx     # 参数一:退出代码 int $0x80       # 调用内核功能


 wanny@wanny-C-Notebook-XXXX:~$ as hello.s -o hello.o
wanny@wanny-C-Notebook-XXXX:~$ ld hello.o -o hello
wanny@wanny-C-Notebook-XXXX:~$ ./hello 
Hello, world.
Hello, world.
   
二,Linux 汇编工具
           Linux 平台下的汇编工具虽然种类很多,但同 DOS/Windows 一样,最基本的仍然
是汇编器、连接器和调试器。

2.1.汇编器
汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标
代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编工具,通常
包含在 binutils 软件包中。GAS 使用标准的 AT&T 汇编语法,可以用来汇编用
AT&T 格式编写的程序:

例如:
 wangzhe@wangzhe-laptop:~$ as -o hello.o hello.s

Linux 平台上另一个经常用到的汇编器是 nasm,它提供了很好的宏指令功能,并
能够支持相当多的目标代码格式,包括 bin、a.out、coff、elf、rdf 等。NASM 采用
的是人工编写的语法分析器,因而执行速度要比 GAS 快很多,更重要的是它使用
的是 Intel 汇编语法,可以用来编译用 Intel 语法格式编写的汇编程序:

例如:
wangzhe@wangzhe-laptop:~$ nasm -f elf hello2.asm 

2.2 链接器
由汇编器产生的目标代码是不能直接在计算机上运行的,它必须经过链接器的处理
才能生成可执行代码。链接器通常用来将多个目标代码连接成一个可执行代码,这
样可以先将整个程序分成几个模块来单独开发,然后才将它们组合(链接)成一个应
用程序。 Linux 使用 ld 作为标准的链接程序,它同样也包含在 binutils 软件包中。
汇编程序在成功通过 GAS 或 NASM 的编译并生成目标代码后,就可以使用 ld 将
其链接成可执行程序了:


例如:
wangzhe@wangzhe-laptop:~$ ld -o hello hello.o

3.调试器
有人说程序不是编出来而是调出来的,足见调试在软件开发中的重要作用,在用汇
编语言编写程序时尤其如此。Linux 下调试汇编代码既可以用 GDB、DDD 这类通
用的调试器,也可以使用专门用来调试汇编代码的 ALD(Assembly Language
Debugger)。
从调试的角度来看,使用 GAS 的好处是可以在生成的目标代码中包含符号表
(symbol table),这样就可以使用 GDB 和 DDD 来进行源码级的调试了。要在生成
的可执行程序中包含符号表,可以采用下面的方式进行编译和链接:


例如:
wangzhe@wangzhe-laptop:~$ as --gstabs -o hello.o hello.s
wangzhe@wangzhe-laptop:~$ ld -o hello hello.o
wangzhe@wangzhe-laptop:gdb -q hello

三,系统调用
          即便是最简单的汇编程序,也难免要用到诸如输入、输出以及退出等操作,而要进
行这些操作则需要调用操作系统所提供的服务,也就是系统调用。除非你的程序只完成
加减乘除等数学运算,否则将很难避免使用系统调用,事实上除了系统调用不同之外,各种
操作系统的汇编编程往往都是很类似的。

          在 Linux 平台下有两种方式来使用系统调用:利用封装后的 C 库(libc)或者通过
汇编直接调用。其中通过汇编语言来直接调用系统调用,是最高效地使用 Linux 内核
服务的方法,因为最终生成的程序不需要与任何库进行链接,而是直接和内核通信。
           和 DOS 一样,Linux 下的系统调用也是通过中断(int 0x80)来实现的。在执行 int
80 指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按
顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax
 中获得。
          所有的系统调用功能号都可以在文件 /usr/include/bits/syscall.h 中找到,为了便
于使用,它们是用 SYS_<name> 这样的宏来定义的,如 SYS_write、SYS_exit 等。例
如,经常用到的 write 函数是如下定义的:
ssize_t write(int fd, const void *buf, size_t count);
该函数的功能最终是通过 SYS_write 这一系统调用来实现的。根据上面的约定,参
数 fb、buf 和 count 分别存在寄存器 ebx、ecx 和 edx 中,而系统调用号 SYS_write
则放在寄存器 eax 中,当 int 0x80 指令执行完毕后,返回值可以从寄存器 eax 中获得。
或许你已经发现,在进行系统调用时至多只有 5 个寄存器能够用来保存参数,难道
所有系统调用的参数个数都不超过 5 吗?当然不是,例如 mmap 函数就有 6 个参
数,这些参数最后都需要传递给系统调用 SYS_mmap:
整理
void * mmap(void *start, size_t length, int prot , int flags, int fd,
off_t offset);
当一个系统调用所需的参数个数大于 5 时,执行 int 0x80 指令时仍需将系统调用功能号保存
在寄存器 eax 中,所不同的只是全部参数应该依次放在一块连续的内存区域里,同时在寄存器
ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值仍将保存在寄存器 eax 中。
由于只是需要一块连续的内存区域来保存系统调用的参数,因此完全可以像普通的
函数调用一样使用栈(stack)来传递系统调用所需的参数。但要注意一点,Linux 采
用的是 C 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后
一个参数先入栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所需的参
数,在执行 int 0x80 指令时还应该将栈指针的当前值复制到寄存器 ebx 中。


四,C语言中内联汇编
          例如:
#include <stdio.h>

int main(void)
{
   int a = 10, b = 0;
   __asm__ __volatile__("movl %1, %%eax;"
   "movl %%eax, %0;"
   :"=r"(b) /* 输出 */
   :"r"(a)  /* 输入 */
   :"%eax");/* 不受影响的寄存器 */
   printf("Result: %d, %d\n", a, b);
}

通常嵌入到 C 代码中的汇编语句很难做到与其它部分没有任何关系,因此更多时
候需要用到完整的内联汇编格式:
__asm__("asm statements" : outputs : inputs : registers-modified);
插入到 C 代码中的汇编语句是以":"分隔的四个部分,其中第一部分就是汇编代码
本身,通常称为指令部,其格式和在汇编语言中使用的

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值