读《Professional Assembly Language》之系统调用
今年是离开校园的第五年,这五年来我一直在从事应用软件的设计、开发工作,大部分时间是在与高级编程语言、设计模式、业务逻辑打交道。它们大多流于表面,久而久之,与技术底层疏远了,诸如计算机组成原理、汇编语言、编译原理、数据结构以及算法慢慢得生疏,时至今日,我已经弄不懂IA-32里有几类寄存器、间接寻址、词法分析是怎么一回事情了。五年是一个契机,趁着下一个五年开始之际,我计划用三个月至半年时想间,重新学习这些知识,以期达到巩固基础,厚积薄发的目的。
学习过程肯定会有问题,找不出问题的学生不是好学生。因此,我把遇到的问题和解决的方法记录下来,便形成了读书笔记。本篇博文便是我在阅读《Professional Assembly Language》一书时,所作的其中一篇读书笔记。《Professional Assembly Language》,中文译作《汇编语言程序设计》,是我学习汇编语言时选择的工具书,该书对于我这种已经有了高级语言的使用经验,又热衷Linux的人来说非常合适。
《Professional Assmebly Language》的第十二章是《Using Linux System Call》,这一章围绕系统调用展开,并对比了C库函数调用。在过往的经历中,我对函数调用的认识全都止于C库函数,这次终于有了对系统调用的初步认识,于是从一开始便对int $0x80的疑惑,在这一章有了一个清晰的答案。
Linux系统调用(system call)的过程是这样的:
- 首先,将要调用的system call number放入到eax寄存器中;
- 然后,将参数按照要求依次放入ebx、ecx等寄存器中;
- 接着,调用int $0x80;
- 最后,从eax寄存器中获取返回值。
整个过程比较简单,–除了获取返回值时可能会遇到复杂的数据结构。
但是,在阅读本章的时候,我发现了两处前后矛盾表述,这两段表述都是讲解系统调用过程的第二步骤,在可以使用几个通用寄存器的问题上,出现了五个和六个的矛盾。先来看第一处,位于英文版第342页:
The EIP, EBP and ESP registers cannot be used, as that would adversely affect the program operation. This leaves five registers available to hold the input values.
这段话表明,ebp等三个寄存器不能用来保存输入值(也就是参数),因此就只有五个寄存器,分别是:ebx、ecx、edx、esi、edi。
坚持读完这个章节,直到最后的Summary,位于英文版360页,又可以看到另外一处表述:
The kernel system calls use register-based method for passing input and output values. The input values are passed to system call in the order they are documented in the man page, The register order used is EBX, ECX, EDX, ESI, EDI, and EBP. The output value is returned in the EAX register.
这段话又指出可以使用六个通用寄存器,除了前文所讲的五个意外,多出一个ebp寄存器。这就叫人纠结了,ebp寄存器都是用还是不用呢?前后两处表述都是言之凿凿,那么哪个是对的呢?
我尝试查看ABI(Applicaiton Binary Interface),但并未找到对这个问题的明确阐述,这个问题暂且当作一个悬案吧,日后再来跟进。