汇编语言是一种低级语言,一个用C&C++写的程序要通过编译器生成相应的汇编程序,在通过汇编器生成相关包含对应机器指令码的目标文件, 在通过链接器将所有的相关的目标文件链接生成一个可执行程序文件。在linux上,目标文件和可执行程序文件的格式都是ELF格式的。
汇编语言程序的编写遵循特定的约定,就像一个C程序必须要有一个main函数一样,这是由特定的编译器和汇编器决定的(它们本质上也只是一个程序而已),不同的汇编器有不同的格式要求,保留的关键字(指示符), x86架构的汇编语言有两种标准, 一种是intel制定的,另一种是AT&T, 两者最大的不同可能是在于操作数的位置,intel标准中的目的操作数在左边, 源操作数在右边, 而AT&T格式的正好相反, 由于linux的GNU一般采用的是AT&T格式,看一个简单的汇编程序,能学到的东西还是很多的。
# cpuid.s Sample program to extract the processor Vendor ID .section .data output: .ascii "The processor Vendor ID is 'xxxxxxxxxxxx'\n" .section .text .globl _start _start: movl $0, %eax cpuid leal output, %edi #movl $output, %edi 两者是等价的 movl %ebx, 28(%edi) movl %edx, 32(%edi) movl %ecx, 36(%edi) #call write system call movl $4, %eax #system call number movl $1, %ebx movl $output, %ecx movl $42, %edx int $0x80 #call exit system call movl $1, %eax movl $0, %ebx # return code int $0x80
2> output 标签(label), 一个label就是一个地址, 类似于C语言中宏, 但是这个地址是多少得由最终链接器在链接的时候来分配。
3> .ascii 数据类型, 这是由不同的汇编器来定义的。还有其它的数据类型
4> .globl 符号的全局属性, 表示外部模块也可以引用这段代码。
5> _start 标签/符号, 类似于C语言中main符号一样,是汇编程序的入口点, 这个入口点也可以在链接的时候指定
6> 常量和内存地址的区别: 如果一条汇编语句中引用的是常量而不是内存地址, 必须在数据前加一个美元符号$以示区别, 否则的话表示的是一个内存单元的数据,而不是地址本身。
7> 引用一个寄存器时,前面要加一个百分号%.
8> linux系统调用的实现是通过一个异常实现的从用户态向内核态切换。 eax寄存器存放的是系统调用号, ebx, ecx, edx, edi, esi是存放系统调用的参数(只有这5个寄存器用于传递参数,如果多于5个的话,一般用栈(内存)来传递),一个子程序的处理结果一般保存在eax寄存器中。
9> 内存寻址方式: disp(base, index, scale).
10> 调用一个子程序, 操作数前面得加个星号*. 如 call *%eax