概述
x86/Debian Linux/gcc。
1 目前学习汇编的步骤
- 怎么将汇编代码添加到汇编程序框架中。
- 理解汇编语句的含义。
- 汇编源代码是怎么转换为可执行代码的。
2 AT&T汇编程序结构
在汇编中,一般具有如下的结构:
- 数据段(.data)
- 附加段(.bss)
- 代码段(.text)
在汇编程序中,数据和附加段可选,但在汇编程序中经常包含有这两个段。代码段必含,是声明指令代码的地方。
(1)定义段
每个段用”.section”或“.标识符”标识。如”.section.data”或”.data”就形象的表示一个数据段。其实对于对于系统来说,它只是一个段,以“.”标识。
(2) 汇编程序入口
当汇编代码被编译成为可执行程序的时候,连接器必须要知道程序入口点在哪里。在GNU汇编器中,_start被定义为汇编程序的入口标识符,_start标识符暗示程序从这里开始运行。
为了让这个入口标识符能被其它文件的汇编代码认识,提供 “.globl”指令实现,即用.globl _start语句声明程序入口标识符。
所以,一个AT&T汇编程序的结构大概如此:
.section .data < initialized data here>
.section .bss < uninitialized data here>
.section .text .globl _start _start: <instruction code goes here> |
在代码段中,汇编代码依靠系统调用来完成一些操作,包括在程序退出之前的一些操作及程序退出操作。
3一个简单的汇编程序
(1) 数据段
.data msg: .ascii "hello, worldn" len= .-msg |
- 形象的标名数据段。
- .ascii为AT&T汇编中的数据类型,类似的数据类型还有“.short”、“.int”、“.long”、“.byte”、“.float”、“.double”、“.comm”。第二条语句叫表示msg作为后续字符串的起始地址。
- len求得字符串的长度。在汇编程序中“.”可以取出当前地址计数器的值,该值是一个常量。
(2) 代码段
代码段主要是用系统调用来完成程序功能。包含“程序入口”、“功能代码”和“程序退出代码”。
[1]程序入口
.text .global _start _start: |
按照GNU AT&T汇编的格式,_start为汇编程序的入口,且由.global保证其它文件内的汇编代码可识别_start入口。
[2]功能代码
_start: movl $4, %eax #Linux system wirte call movl $1, %ebx #STDOUT movl $msg, %ecx #Give string start address to %ecx movl $len, %edx #Give string lenght to %edx int $0x80 #Software interrupt |
int指令中的立即数0x80是一个参数,在异常处理程序中要根据这个参数决定如何处理,在Linux内核中int $0x80这种异常称为系统调用(System Call)。内核提供了很多系统服务供用户程序使用,但这些系统服务不能像库函数(比如printf)那样调用,因为在执行用户程序时CPU处于用户模式,不能直接调用内核函数,所以需要通过系统调用切换CPU模式,经由异常处理程序进入内核,用户程序只能通过寄存器传几个参数,之后就要按内核设计好的代码路线走,而不能由用户程序随心所欲,想调哪个内核函数就调哪个内核函数,这样可以保证系统服务被安全地调用。在调用结束之后,CPU再切换回用户模式,继续执行int $0x80的下一条指令,在用户程序看来就像函数调用和返回一样。[Linux C 编程一站式学习]
在int $0x80引发的系统调用中,在eax寄存器内的值表示系统调用号,内核需要通过eax判断用户要调哪个系统调用。4代表linux write system call,1代表linux exits ystem call。ebx的值是传给_exit的参数,表示退出状态。大多数系统调用完成之后会返回用户空间继续执行后面的指令,而_exit系统调用比较特殊,它会终止掉当前进程,而不是返回用户空间继续执行。And so on。
在linux 写系统调用(eax值为4)下,其它的寄存器内所存值也会代表一些含义:
- ebx包含要写向的文件描述符(STANDOUT:1,STDIN:0,STDERR:2)。
- ecx包含子符串的起始地址。
- edx包含ecx字符串的长度。
故而,在为写操作准备好所有的条件之后。int $0x80经执行引起的软件中断后,照eax内存的值就访问linux内核的一些函数,向控制台(ebx:1)输出字符串(ecx:msg)。
[3]程序退出
movl $1, %eax #Linux systemexit call movl $0, %ebx #Acceptprogram exit value int $0x80 #software interrupt |
Linux汇编程序的退出也是依靠系统调用来完成。此时寄存器eax中的值应该为1,ebx的值为程序退出后返回给shell的值。这几句汇编程序完成_exit()调用。
(1)、(2)这段汇编代码相当于以下这段C代码:
#include <unistd.h> char msg[14] = "hello, worldn"; #define len 14 int main(void) { write(1, msg, len); _exit(0); } |
4 编译执行AT&T linux汇编
加入以上各部分组成的代码文件名为,learn.s,则编译执行过程为:
as -o learn.o learn.s [生成learn.o文件]
ld -o learn learn.o [生成learn可执行文件]
./learn [ 得程序执行结果]
hello, world
5 总结
关于涉及到的AT&T汇编的语法:
- 立即数前面需要加$。
- 直接引用符号常数,在符号常数前加$表示引用符号常数的地址。
- movl %eax, %ebx,源操作数为eax内的值,目的操作数为ebx的值。源、目的操作数符合从左到右的操作顺序。
- 寄存器前面要加%。
- Linux 下用vim编辑器编辑AT&T代码时#表示注释符号。
初学汇编,在了解汇编程序框架后,如果不知代码段应该写什么内容,不明白要用哪一个寄存器来操作时。应该查查各寄存器在系统调用软件中断发生时(int $0x80)要发生的操作所对应的寄存器的值。这样,起码就能编写“hello world!”的入门代码了。
当然,要了解整个汇编代码的机制还得多编写,达到只需查询就可完成功能的目标。目前根据这个程序得到的经验是:汇编代码可以依靠系统调用软件中断来调用内核的函数,系统调用的类型由eax寄存器你的值。我们需要提前配置好eax及相关寄存器的值,从而实现咱的目标功能。一般是eax寄存器里面的值决定软件中断后linux系统调用的类型(eax:4linux write system call,eax:1 linux exit system call),然后其它的寄存器配合这种统调用。[好像总结得有点浅显]
我们用C标准I/O库函数最终也是通过系统调用把I/O操作从用户空间传给内核,然后让内核去做I/O操作。
ASNote Over。
最后
以上就是奋斗外套为你收集整理的汇编版hello world (write()与_exit()系统调用) [李园7舍_404]1 目前学习汇编的步骤2 AT&T汇编程序结构3一个简单的汇编程序4 编译执行AT&T linux汇编5 总结的全部内容,希望文章能够帮你解决汇编版hello world (write()与_exit()系统调用) [李园7舍_404]1 目前学习汇编的步骤2 AT&T汇编程序结构3一个简单的汇编程序4 编译执行AT&T linux汇编5 总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复