freebsd x86汇编语言

上传人:小** 文档编号:89124819 上传时间:2019-05-18 格式:DOC 页数:143 大小:335.50KB
返回 下载 相关 举报
freebsd x86汇编语言_第1页
第1页 / 共143页
freebsd x86汇编语言_第2页
第2页 / 共143页
freebsd x86汇编语言_第3页
第3页 / 共143页
freebsd x86汇编语言_第4页
第4页 / 共143页
freebsd x86汇编语言_第5页
第5页 / 共143页
点击查看更多>>
资源描述

《freebsd x86汇编语言》由会员分享,可在线阅读,更多相关《freebsd x86汇编语言(143页珍藏版)》请在金锄头文库上搜索。

1、第11章 x86 汇编语言目录11.1 概述11.2 工具11.3 系统调用11.4 返回值11.5 建立可移植的代码11.6 编写第一个程序11.7 编写 UNIX 过滤程序11.8 缓存 I/O11.9 Command Line Arguments11.10 UNIX 中的环境11.11 文件处理11.12 One-Pointed Mind11.13 Using the FPU11.14 忠告11.15 致谢本章节由 G. Adam Stanislav 撰写。 翻译: 。11.1 概述涉及 UNIX 下的汇编语言的相关资料很少。 通常我们都假设几乎没有人希望用到它,因为不同的 UNIX 可

2、能是在不同的处理器上运行的, 所以为了移植性的考虑所有的东西都应该用 C 来实现。实际上, C 语言的移植性是非常神秘的。即使 C 程序在不同的 UNIX 之间移植的时候需要进行修改,但这些修改绝对和在什么处理器上运行无关。 特别明显地是,这样的程序充满了在编译时对于系统依赖的条件描述。即使我们希望所有的 UNIX 软件都应该用 C 语言或者是其他的高级语言编写, 但是我们仍然需要掌握汇编语言的程序员: 不然谁将完成 C 语言函数库中直接访问内核的部分?在本章节,我将尝试着说明如果用汇编语言编写 UNIX 程序,特别是在 FreeBSD 上的程序。这个章节不会讲解汇编语言的基础知识。现在已经有

3、很多相关的资料了(如果你想寻找完整的关于汇编语言的在线课程, 请看 Randall Hyde 的 汇编语言艺术;如果你想选择一本书, 那么去看看 Jeff Duntemann 的 循序渐进学汇编) 不过, 一旦你掌握了这个章节的内容,任何汇编语言的程序员, 将能够在 FreeBSD 上高效、快速地编写程序。11.2 工具11.2.1 汇编器汇编语言编程最重要的工具是汇编器, 它将汇编语言代码转换成机器语言。在 FreeBSD 中有两个完全不同的汇编器。 一个是 as(1), 使用传统的UNIX汇编语法, 它是随系统发布的。另外一个是 /usr/ports/devel/nasm。 它使用 Int

4、el 的语法规范, 其最大的好处是可以在许多操作系统上汇编代码。 它需要你单独安装,不过它是完全免费的。本章节使用 nasm 的语法规范,这是因为许多从其他操作系统移植到 FreeBSD 的汇编语言可以更好地被理解。 不过, 坦白地说,这是因为我更习惯这样的语法规范。11.2.2 连接器像任何编译器一样, 汇编器的输出需要被连接后才能成为可执行文件。标准的连接器 ld(1) 随 FreeBSD 发布。 它可以和以上任何一个汇编器一起完成代码组装。11.3 系统调用11.3.1 默认的调用规范通常, FreeBSD 的内核使用 C 语言的调用规范。 此外, 虽然我们使用 int 80h 来访问内

5、核, 但是我们常常通过调用一个函数来执行 int 80h, 而不是直接访问。这个规范是非常方便的, 比 Microsoft 的 MS-DOS 上使用的规范更加优越。 为什么呢? 因为 UNIX 的规范允许任何语言所写的程序访问内核。汇编语言也可以这样做, 比如, 我们可以编写一个文件:kernel: int 80h ; Call kernel retopen: push dword mode push dword flags push dword path mov eax, 5 call kernel add esp, byte 12 ret这是一种非常清晰而易于移植的编码方法。 如果你需要将

6、代码移植到一个使用和 FreeBSD 完全不同中断或参数传递方式的 UNIX 系统,那么你所需要做的仅仅是改变那一段内核程序。但是汇编程序员喜欢使用一些技巧来削减程序执行所需的时钟周期数。 以上的例子需要一个 call/ret 组合, 我们可以通过压栈 push 一个额外的双字节来去除它。open: push dword mode push dword flags push dword path mov eax, 5 push eax ; Or any other dword int 80h add esp, byte 16在上一个 open 的例子中, 我们放在 EAX 中的数字 5 表示了

7、一个内核函数。11.3.2 另一种调用规范 FreeBSD 是一个非常灵活的系统。 它提供了访问内核的其他方式。 但是,如果你要用到它, 你的系统必须安装 Linux emulation。 Linux 是一个类 UNIX 操作系统。 但是,它的内核在传递参数的时候, 使用和 MS-DOS 相同系统调用规范。 比如在 UNIX 的规范中, 代表内核函数的数字存放在 EAX 中。 但是在 Linux 中, 参数不进行压栈而是存放在 EBX, ECX, EDX, ESI, EDI, EBP:open: mov eax, 5 mov ebx, path mov ecx, flags mov edx,

8、mode int 80h这种做法与 UNIX 的常规做法相比有一个严重的缺点,至少现在汇编语言程序需要注意的: 每次进行系统调用,你必须对寄存器的值进行压栈和出栈操作, 这让你的程序变得冗长而效率低下。 因此, FreeBSD 提供给你了一个其他的选择。如果你确实选择了 Linux 的规范, 你必要让你的系统知道这一点。在你的程序完成汇编和连接之后, 你需要对可执行文件进行标识:%brandelf -f Linux filename11.3.3 你该选择哪个规范?如果你专门为 FreeBSD 写代码, 你必须使用 UNIX 的规范: 因为它快速, 你可以在寄存器中存取全局变量, 而且不需要进行

9、可执行的标识,更不需要在你的系统上安装 Linux emulation。如果你想做一些可以在 Linux 上运行的程序, 并且你打算尽可能地给 FreeBSD 用户提供最有效率地程序。 我将在我讲解完汇编基础之后, 告诉你怎样完成这样地程序。11.3.4 系统调用号要通知内核你调用了什么系统服务, 将代表调用的数字放入 EAX 中。 当然, 你需要知道那些系统调用号代表了什么。11.3.4.1 syscalls 文件系统调用号列在了 syscalls 文件里面。 locate syscalls 去寻找这个文件的不同格式, 它们都是从 syscalls.master 中自动生成的。你可以在 /u

10、sr/src/sys/kern/syscalls.master 下找到这个描述默认情况下 UNIX 系统调用规范的文件。如果你需要使用在 Linux emulation 中使用的规范, 请参阅 /usr/src/sys/i386/linux/syscalls.master。注意: FreeBSD 和 Linux 不仅仅是使用不同的调用规范,有时候它们也使用不同的系统调用号来表示相同函数。syscalls.master 描述了如何使用这些调用:0 STD NOHIDE int nosys(void); syscall nosys_args int1 STD NOHIDE void exit(in

11、t rval); exit rexit_args void2 STD POSIX int fork(void); 3 STD POSIX ssize_t read(int fd, void *buf, size_t nbyte); 4 STD POSIX ssize_t write(int fd, const void *buf, size_t nbyte); 5 STD POSIX int open(char *path, int flags, int mode); 6 STD POSIX int close(int fd); etc.最左边的一列告诉我们需要放入 EAX 的数字。最右边的一

12、列告诉我们那些需要被压栈 push 的参数,它们的压栈 push 顺序是 从右到左。例如,要打开 open 一个文件, 我们需要先首先压栈 push 模式字 mode, 然后是压栈标志字 flags, 然后是压栈保存路径 path 的地址。11.4 返回值如果系统调用没有返回一些数值, 那么在很多情况下并没有太多用处。这些返回值包括:一个打开的文件的文件描述符、 一个从缓存里读取的字节, 或者系统时间等等。此外, 如果错误出现, 系统需要通知我们。 这些错误包括:文件不存在、系统资源耗尽,或者我们传递了一个错误的参数等等。11.4.1 联机手册传统情况下,寻找 UNIX 下不同系统调用的地方是

13、手册页。 FreeBSD 会在手册页的第2节描述系统调用,有的时候会在第三节。例如, open(2) 所描述的:如果执行成功, open() 将返回一个表示文件描述符的非负整数。 如果执行失败, 它会返回 -1 并且设置 errno 来标识错误。刚刚接触 UNIX 和 FreeBSD的汇编程序员或许会马上提出一个难题: 那些 errno 在哪里?我怎么才能找得到它们?注意: 这些在手册页中展示的信息仅针对 C 语言。 汇编程序员则需要更多的信息。11.4.2 返回值在哪里?很不幸, 它有依赖性。 大部分系统调用都使用 EAX 但不是所有的系统调用都回这样。 不过当第一次使用系统调用的时候,直接

14、在 EAX 检查返回值是个不错的查找方法。 如果它不在那里,你再需要深入调查。注意: 我知道一个 SYS_fork 的系统调用将返回值存放在 EDX 中。而我曾经用过的其他调用都使用 EAX 存放返回值,但是我目前还没有用过所有的系统调用。提示: 如果你哪里都找不到答案, 去学习 libc 代码吧,去看看它如何访问内核的。11.4.3 哪里可以找到 errno?实际上,没什么地方。errno 不属于 UNIX 内核, 而是 C 语言中的一部分。 当直接访问内核服务的时候, 错误代码返回到 EAX 中,也就是那个通常用来保存返回值得寄存器。这个处理方式非常有意义。 如果这里没有错误, 这里就没有错误代码。如果这里有错误,这里就没有返回值。 使用一个寄存器可以涵盖以上任意一种情况。11.4.4 判断错误的出现当使用标准的 FreeBSD 调用规范时,标志位 carry flag 在程序运行成功时被清零,在失败时被置位。当使用 Linux emulation 模式的时候, EAX 中的有符号数反映了执行结果。 当为非负数时,表示执行成功, 并包含了返回值。 如果失败,返回值即为负数, 如 -errno。11.5 建立可移植的代码一般说来, 可移植性并非汇编语言的长项。 然而,写出能够在不同平台上执行的汇编代码仍然是可能的事情, 特别是在使用 nasm 的时候。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 商业/管理/HR > 管理学资料

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号