Linux汇编初识

linux汇编  

2011-06-11 20:29:02|  分类: x86与arm |  标签:movl  汇编器  linux  代码  att   |举报 |字号 订阅


        由于我喜欢一些底层的东西,不由得想弄一下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 代码中的汇编语句是以":"分隔的四个部分,其中第一部分就是汇编代码

本身,通常称为指令部,其格式和在汇编语言中使用的


原文地址:
http://blog.163.com/zhe_wang_2009/blog/static/172282121201151175619458/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值