c linux工具链,Linux on Power中的GNU C/C++工具链(Cont.)

检查默认的链接器脚本的工作可以通过一个链接器选项发送到标准错误设备上。使用一个简单的

"hello, world" 程序,加上 verbose

选项,您就可以捕获这些内部的命令:

$ cc -o hello hello.c -Wl,--verbose 2> hello.ldscript

这会创建一个 256

行的脚本,它可以处理最常见的链接情况(我们期望如此)。AIX

开发人员应该注意这是 –bbindcmds: 选项的结果,不过我们早已说过,AIX

链接器要更加自动化。

因此您可能会问:为什么会有人对这种功能感兴趣呢?有些开发工作需要使用一些

"链接器欺骗(linker trick)"

的技巧,这是另外一种声明项目对如何构建应用程序和分布内存创建约束的方法。这种需要可以通过聪明地链接编辑操作实现,GNU

链接器的命令行选项就提供了这种机制。

GNU 汇编程序

汇编程序的任务是接受使用人们可读的格式编写的输入文件,并生成包含机器级指令的文件。然后将一个或多个文件中的对象代码传递给链接器,后者负责生成可执行模块。GNU

汇编程序可以支持很多平台;虽然每个平台都可以使用很多命令行选项,但是也有一些平台只能支持一些系统特有的特性。

与其他平台一样, GCC

驱动器可以接受使用高级语言编写的输入文件--

它可以产生汇编代码,然后再使用 GNU

汇编程序转换成目标代码。幸运的是,大部分开发人员都不需要直接使用汇编语言,GCC

驱动器会为开发人员隔离这些具体的实现细节。然而,当需求提高时,理解如何与汇编程序进行交互会非常有用;GCC

可以让汇编程序接受一些使用逗号分隔的选项来支持这种功能,它使用的语法为

-Wa [选项]。例如:

$ 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 的 “GNU 链接器” 一节中,我们曾经介绍了 ELF

对象文件的一些段;这些段就是链接器最感兴趣的地方,它们就是用来生成可执行模块的。然而,几乎编译器所生成的每个对象文件中都有这些有趣的段,它们构成了您的程序的各个部分(使用对象代码格式),如表

6

所示,因此链接器知道如何对它们进行合并,从而创建一个程序或共享模块。

表 6. 二进制文件的组织

段 描述.text 可执行指令(汇编代码所转换成的指令)通常就保存在这个段中。只读常量(例如字符串常量)也可以在这个段中找到。这个平台上的文本段是只读的,这就意味着程序不能对自己进行修改;这个属性允许可执行文件的文本区可以在此程序的所有运行实例之间共享。例如,bash

shell 的每个正在运行的拷贝都会共享 bash 文件的 .text

段的一份拷贝。

.data 这就是为可执行程序保存初始化过的数据的地方。从定义中我们可以看出,数据段是可读写的,进程可以对这个段中的内容(在内存中)如何操作进行完全控制。举例来说,C

语言中那些使用编译时的值进行初始化的全局变量都可以在这个段中找到。

.bss bss

段中包含了尚未初始化的数据;这个段不在对象文件中占据任何空间,但是它定义了一段内存区域,它们在运行时被清零。C

语言中那些没有给定初始化值的全局变量在程序启动时都被定义为

0;这些变量都可以在 bss 段中找到。

链接编辑器会从指定的对象文件中搜集所有的 .text

段的内容,并合并它们来创建可执行程序的代码,所有的 .data

段都被搜集起来用来创建最终的数据段,所有的 .bss

段被合并起来创建最终的 bss

段。还要注意,每个可执行模块都包含自己的段;例如,共享模块就有自己的

.text 段和 .data 段。

汇编版本的 Hello, World!

考虑一下这个 32 位的非 PIC 汇编版本的 "Hello, World!" 它首次出现在

Hollis Blanchard 所编写的“PowerPC assembly: Introduction to

assembly on the PowerPC” 一文中(developerWorks,2002 年 7

月):

清单 2. ia32 assembly

.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 背景的程序员来说会感觉非常熟悉。

检查默认的链接器脚本的工作可以通过一个链接器选项发送到标准错误设备上。使用一个简单的

"hello, world" 程序,加上 verbose

选项,您就可以捕获这些内部的命令:

$ cc -o hello hello.c -Wl,--verbose 2> hello.ldscript

这会创建一个 256

行的脚本,它可以处理最常见的链接情况(我们期望如此)。AIX

开发人员应该注意这是 –bbindcmds: 选项的结果,不过我们早已说过,AIX

链接器要更加自动化。

因此您可能会问:为什么会有人对这种功能感兴趣呢?有些开发工作需要使用一些

"链接器欺骗(linker trick)"

的技巧,这是另外一种声明项目对如何构建应用程序和分布内存创建约束的方法。这种需要可以通过聪明地链接编辑操作实现,GNU

链接器的命令行选项就提供了这种机制。

GNU 汇编程序

汇编程序的任务是接受使用人们可读的格式编写的输入文件,并生成包含机器级指令的文件。然后将一个或多个文件中的对象代码传递给链接器,后者负责生成可执行模块。GNU

汇编程序可以支持很多平台;虽然每个平台都可以使用很多命令行选项,但是也有一些平台只能支持一些系统特有的特性。

与其他平台一样, GCC

驱动器可以接受使用高级语言编写的输入文件--

它可以产生汇编代码,然后再使用 GNU

汇编程序转换成目标代码。幸运的是,大部分开发人员都不需要直接使用汇编语言,GCC

驱动器会为开发人员隔离这些具体的实现细节。然而,当需求提高时,理解如何与汇编程序进行交互会非常有用;GCC

可以让汇编程序接受一些使用逗号分隔的选项来支持这种功能,它使用的语法为

-Wa [选项]。例如:

$ 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 的 “GNU 链接器” 一节中,我们曾经介绍了 ELF

对象文件的一些段;这些段就是链接器最感兴趣的地方,它们就是用来生成可执行模块的。然而,几乎编译器所生成的每个对象文件中都有这些有趣的段,它们构成了您的程序的各个部分(使用对象代码格式),如表

6

所示,因此链接器知道如何对它们进行合并,从而创建一个程序或共享模块。

表 6. 二进制文件的组织

段 描述.text 可执行指令(汇编代码所转换成的指令)通常就保存在这个段中。只读常量(例如字符串常量)也可以在这个段中找到。这个平台上的文本段是只读的,这就意味着程序不能对自己进行修改;这个属性允许可执行文件的文本区可以在此程序的所有运行实例之间共享。例如,bash

shell 的每个正在运行的拷贝都会共享 bash 文件的 .text

段的一份拷贝。

.data 这就是为可执行程序保存初始化过的数据的地方。从定义中我们可以看出,数据段是可读写的,进程可以对这个段中的内容(在内存中)如何操作进行完全控制。举例来说,C

语言中那些使用编译时的值进行初始化的全局变量都可以在这个段中找到。

.bss bss

段中包含了尚未初始化的数据;这个段不在对象文件中占据任何空间,但是它定义了一段内存区域,它们在运行时被清零。C

语言中那些没有给定初始化值的全局变量在程序启动时都被定义为

0;这些变量都可以在 bss 段中找到。

链接编辑器会从指定的对象文件中搜集所有的 .text

段的内容,并合并它们来创建可执行程序的代码,所有的 .data

段都被搜集起来用来创建最终的数据段,所有的 .bss

段被合并起来创建最终的 bss

段。还要注意,每个可执行模块都包含自己的段;例如,共享模块就有自己的

.text 段和 .data 段。

汇编版本的 Hello, World!

考虑一下这个 32 位的非 PIC 汇编版本的 "Hello, World!" 它首次出现在

Hollis Blanchard 所编写的“PowerPC assembly: Introduction to

assembly on the PowerPC” 一文中(developerWorks,2002 年 7

月):

清单 2. ia32 assembly

.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 背景的程序员来说会感觉非常熟悉。

附:

Q:GNU链接器几个开关项的解释:-lm -lc -lgcc

A:

-lm 代表链接器将连接GCC的数学库libm.a -lc 代表链接器将连接GCC的标准C库libc.a -lgcc 代表链接器将连接GCC的支持库libgcc.a 在连接时,这些库的排列顺序一般为: -lm -lc -lgcc 这些库都有不同版本,根据 Endian 模式, ARM/THUMB/Interwork模式 等进行区分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值