首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

GNU 汇编程序

GNU 汇编程序

GNU 汇编程序汇编程序的任务是接受使用人们可读的格式编写的输入文件,并生成包含机器级指令的文件。然后将一个或多个文件中的对象代码传递给链接器,后者负责生成可执行模块。GNU                汇编程序可以支持很多平台;虽然每个平台都可以使用很多命令行选项,但是也有一些平台只能支持一些系统特有的特性。
与其他平台一样, GCC 驱动器可以接受使用高级语言编写的输入文件 -- 它可以产生汇编代码,然后再使用 GNU                汇编程序转换成目标代码。幸运的是,大部分开发人员都不需要直接使用汇编语言,GCC                驱动器会为开发人员隔离这些具体的实现细节。然而,当需求提高时,理解如何与汇编程序进行交互会非常有用;GCC                可以让汇编程序接受一些使用逗号分隔的选项来支持这种功能,它使用的语法为 -Wa [选项]。例如:
1
$ gcc -O -Wa,-Z,-v foo.c -o foo




这条命令使用 GCC 驱动器将文件 foo.c 编译成二进制文件 foo,并打印汇编程序的版本,即便碰到错误也是如此。
虽然您可能会发现在 Linux on POWER                上,GCC、链接器、汇编程序等的用法都与其他平台非常类似,但是汇编语言本身却有很大的区别。注意一些细节问题:RISC 架构与 CISC                架构相比,通常需要多条指令来完成一个特定的任务;POWER 也不例外。如果您查看一下 GCC 所生成的汇编代码,就会注意到其中会有很多 load 和                store 指令,同时还有其他类型的指令。熟悉汇编语言的一种有用的方法是使用高级语言(例如 C)来编写代码,并使用 GCC 的                -S 选项来保存所生成的汇编文件,或者使用 objdump 对目标代码进行反汇编。
二进制文件的组织
在上文中有关 ld 的  一节中,我们曾经介绍了 ELF                对象文件的一些段;这些段就是链接器最感兴趣的地方,它们就是用来生成可执行模块的。然而,几乎编译器所生成的每个对象文件中都有这些有趣的段,它们构成了您的程序的各个部分(使用对象代码格式),如  所示,因此链接器知道如何对它们进行合并,从而创建一个程序或共享模块。
表 6. 二进制文件的组织段描述.text可执行指令(汇编代码所转换成的指令)通常就保存在这个段中。只读常量(例如字符串常量)也可以在这个段中找到。这个平台上的文本段是只读的,这就意味着程序不能对自己进行修改;这个属性允许可执行文件的文本区可以在此程序的所有运行实例之间共享。例如,bash                            shell 的每个正在运行的拷贝都会共享 bash 文件的 .text 段的一份拷贝。.data这就是为可执行程序保存初始化过的数据的地方。从定义中我们可以看出,数据段是可读写的,进程可以对这个段中的内容(在内存中)如何操作进行完全控制。举例来说,C                            语言中那些使用编译时的值进行初始化的全局变量都可以在这个段中找到。.bssbss                            段中包含了尚未初始化的数据;这个段不在对象文件中占据任何空间,但是它定义了一段内存区域,它们在运行时被清零。C                            语言中那些没有给定初始化值的全局变量在程序启动时都被定义为 0;这些变量都可以在 bss 段中找到。
链接编辑器会从指定的对象文件中搜集所有的 .text 段的内容,并合并它们来创建可执行程序的代码,所有的 .data                段都被搜集起来用来创建最终的数据段,所有的 .bss 段被合并起来创建最终的 bss                段。还要注意,每个可执行模块都包含自己的段;例如,共享模块就有自己的 .text 段和 .data 段。
汇编版本的 Hello, World!
考虑一下这个 32 位的非 PIC 汇编版本的 "Hello,                World!" 它首次出现在 Hollis Blanchard 所编写的                一文中(developerWorks,2002 年 7 月):
清单 2. ia32                assembly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.data                  # section declaration - variables only
.align    3     # make double-word aligned
msg:
    .string "Hello, world!\n"
    len = . - msg       # length of our dear string
.text                  # section declaration - begin code
.align    3            # ensure alignment
    .global _start
_start:
# write our string to stdout
    li      0,4         # syscall number (sys_write)
    li      3,1         # first arg: file descriptor (stdout)
                        # second arg: pointer to message to write
    lis     4,msg@ha    # load top 16 bits of &msg
    addi    4,4,msg@l   # load bottom 16 bits
    li      5,len       # third argument: message length
    sc                  # call kernel
# and exit
    li      0,1         # syscall number (sys_exit)
    li      3,1         # first argument: exit code
    sc                  # call kernel




注意数据段位于最上面,其中定义了 printf 要打印的字符串 -- 它可以于 .text                段中的内容一样简单,因为它是被当作一个常量来对待的。
文本段是有许多指令组成的,因为这个程序要使用几个系统调用(而不是使用 libc 中的程序,例如 printf)来完成工作。write                系统调用是在寄存器 0 中指定的,目标文件是在寄存器 3 中指定的,指向字符串的指针是在寄存器 4 中指定的,字符串的长度是在寄存器 5                中指定的。在输出字符串之后,又要将寄存器领设置为 exit 的系统调用,并传递值 "1" 作为退出代码。
这个例子会被传递给汇编程序,从而创建一个对象文件;所生成的对象文件又被传递给链接器,后者生成一个可执行文件的映像。这一系列操作对于具有 AIX 和                Linux 背景的程序员来说会感觉非常熟悉。
汇编中的 CFI 指令
如果您发现自己要将 C++ 程序和汇编程序合并在一起,那么就可以使用                CFI(Call Frame Information)指令来使用异常处理的支持。这些指令会生成一些堆栈展开信息,这样使用汇编编写的程序就可以与使用                C++ 或其他语言编写的程序很好地结为一体了。
GNU 汇编程序中所支持的 CFI 指令在“CFI support for GNU assembler (GAS)” Web                页面上进行了详细的介绍。 对于 Linux on POWER                来说,CFI 指令所感兴趣的是 start_proc 和                end_proc。当把这些指令放到汇编代码中的每个函数两端时,就会生成 .eh_frame 信息。
更多的 Binutils
除了链接器和汇编程序之外,有些开发人员还会发现其他 binutils                也非常有用。在本文中我们不会讨论每个工具的功能,这在 GNU Binary Utilities 的 Web 站点上已经详细介绍过了,下一节的讨论将局限于两个类似的工具。objdump 和                readelf 的出现或多或少都是非常自然的,这取决于您所使用的平台。AIX 开发人员可以认识 objdump 的风格,ELF 的老用户也早就对                readelf 很熟悉了。这两个工具在 Linux on POWER 上都可以使用。
objdump熟悉 dump 命令的 AIX 开发人员可能会希望了解有关 objdump                    的知识。这个工具提供了类似的功能,而且功能比您所熟悉的功能更加丰富。objdump                    的一个有用特性是可以对对象文件进行反汇编,并查看机器代码。不过,这并不适合对汇编程序进行反馈。readelf另外一个有用的工具是                    readelf,它可以显示符号、段信息、二进制文件格式的信息等等。这在分析编译器如何从源代码创建二进制文件时非常有用,尤其是对于所链接的共享对象更是如此。AIX                    开发人员会发现这与使用 dump 命令来查看所加载的段信息是一致的。结束语Linux on POWER 上的开发与 Linux on x86 架构以及在 POWER 架构上运行的 AIX                上的开放有所不同。通常来说,这些不同对于开发人员来说并不明显,因为 GNU                软件的目标就是实现可移植性。然而,在某些情况中,需要更多信息,我们试图提供这些信息,因为它们是属于 GNU 开发工具链的,包括 GCC                编译器、GNU 链接器和汇编程序,以及其他 GNU binutils。
我们已经回顾了 GNU Compiler 的功能,以及它的能力:不仅可以用来编译代码,而且可以用来控制其他                binutils。我们对链接器和汇编程序所采用的内部格式和操作进行了回顾,这重点是为了那些熟悉其他架构和操作系统的读者准备的。在本文整篇文章的内容中参考了很多其他有关该主题的资料, 中都列出了。我们在撰写本文时参考了当时最新的 GCC                手册。可能在您阅读本文时已经有了更新的发布版。请查看参考资料,获取最新的 GCC 手册。
返回列表