了解x86汇编文件

好,下面让我们看官方手册学习汇编:PC Assembly Lanugage (mit.edu)

哈哈,开玩笑的!实际上都是些上学期学的比较基础的指令。

以下面这段代码为例:

 # test1.s 
 .section .data 
 .section .text 
 .globl _start 
 _start: 
     movl $1, %eax 
     movl $3, %ebx 
     int $0x80

这段汇编代码,我相信大家能看懂一些,但我还是要详细地解释一下。

我们先对它进行汇编操作:

 gcc -c test1.s -o test1.o

然后再进行链接操作:

 gcc test1.o -o test1

是不是懵B了?为什么会报错?从报错信息中,我们可以看出两个错误的原因:

  • _start函数出现了双重定义

  • _start函数出现了未定义的引用main

 /usr/bin/ld: test1.o: in function `_start':
(.text+0x0): multiple definition of `_start'; /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o:(.text+0x0): first defined here
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
collect2: error: ld returned 1 exit status

实际上,_start函数便是链接器默认的程序入口。也就是说,通过链接器ld链接的ELF可执行文件运行后执行的第一条指令就是从_start开始的第一条指令!!!而联系到我们平时写的C语言代码,都是从main函数开始执行的。那么,我们推测,通过gcc默认链接的ELF可执行文件,一定有一段从_start跳转到main的代码。

exercise1:请反汇编Scrt1.o,验证下面的猜想(加-r参数,显示重定位信息)

所以结合报错(报错的第二行有个Scrt1.o),我们不难猜测出错误发生的原因:gcc在链接的时候,默认会把Scrt.o这个ELF可重定位文件同时链接到我们的程序里,而这个Scrt1.o里面包含了_start函数,这个函数的作用正是跳转到main函数。而我们的代码里面也出现了一个_start函数,并且不存在main,所以导致了两个报错!!!

好,回到正题。干脆不用gcc链接了,直接改用进行ld链接:

 njucs> ld test1.o -o test1 
 njucs> ./test1 
 njucs> echo $? 
 3

在执行编译好的程序之后,我们echo $?来查看程序返回值,发现是3。我们在ICS里面学到,int $0x80是执行系统调用(System Call)的软中断。而%eax中的1是系统调用号,通过简单查阅可知,1是_exit系统调用的系统调用号,作用是结束当前进程,并返回。%ebx里面存放了退出时返回的值。所以会显示出3。

这就是我们这个简单的汇编程序执行的全过程。那么借助这个程序,我来解释几个关键的符号:

  • 通过一个“点”开头的东西比如.section.data叫做汇编语言地伪指令,用来指导汇编器进行布局和汇编。

  • .section把这个程序划分为不同的段(section),比如代码段,数据段,相信大家都了解。

  • .data是声明一个可读可写数据段,存放的数据比如全局变量。(在这个代码中,没有全局变量,所以是空的!)。

  • .text段是声明一个代码段,不可以写,只可以读和执行,程序的代码部分在这个段里。

  • .globl说明这是一个全局函数或者全局变量,决定是否可以被其他模块引用(有globl的就可以被其他模块引用)。

好吧,上面这段文字就是给大家介绍一下汇编语言的文件是怎么写的。ICS更多偏重于学习一些汇编指令,而本阶段的OS则是会用到一些真正的汇编文件。当然,这些不是最全的,不过我相信经过讲解,大家遇到问题都可以合理去搜索,实践并且分析出答案。

这个是GCC官网,我们可以查阅相关内容(全英文有点难,可以挑战一下自己)。还可以通过man gcc或者gcc --help来学习。

下面推荐另外一套工具链:https://asmtutor.com/#top

Last updated