gcc中的旁枝末节

作业:1、gcc编译器的优化选项的具体内容
2、查询清楚可重入目标文件与不可重入目标文件
3、了解ELF文件的特点
4、了解-static静态编译 编译器的好处以及工作内容


gcc编译器的优化选项的具体内容

-O1:
会进行中等函数的优化,使代码的体积更小,运行时间更短。
但是那种大型的函数,O1是不会对其进行操作的,因为会花费大量的时间。
其中,他的具体的一些优化项部分如下表。
这里i写图片描述

-O2:
会愿意花费更长的时间来实现更好的代码性能。
但是也不会执行循环函数的展开以及内联函数的优化
同时,他也只乐意时间和代码性能间的转换,对于空间和速度之间的转化他觉得没有意义。

-O3:
会进行更进一步的优化,将-O2所指定的优化都打开

可重入目标文件与不可重入目标文件

此类分法主要出现在多任务进程里。
例如,在多任务进程中。执行某个文件的时候,遇到某信号需要被处理。指令就中断,去处理那个信号。当处理完那个信号继续回来的时候,继续回来完成刚才的指令序列。如果恢复中断,以及重新执行指令序列的过程中,指令所依赖的坏境都没有变化,则成为可重入目标文件。
但是如果环境已经发生了变化,则是不可重入目标文件。

ELF文件的简介

ELF格式的文件,在计算机科学中,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。即可执行连接文件格式

(1)ELF文件类型:
a)可重定位文件:用户和其他目标文件一起创建可执行文件或者共享目标文件,例如lib*.a文件。
b)可执行文件:用于生成进程映像,载入内存执行,例如编译好的可执行文件a.out。
c)共享目标文件:用于和其他共享目标文件或者可重定位文件一起生成elf目标文件或者和执行文件一起创建进程映像,例如lib*.so文件。

(2)ELF文件作用:
ELF文件参与程序的连接(建立一个程序)和程序的执行(运行一个程序),所以可以从不同的角度来看待elf格式的文件:
a)如果用于编译和链接(可重定位文件),则编译器和链接器将把elf文件看作是节头表描述的节的集合,程序头表可选。
b)如果用于加载执行(可执行文件),则加载器则将把elf文件看作是程序头表描述的段的集合,一个段可能包含多个节,节头表可选。
c)如果是共享文件,则两者都含有。

1、ELF文件的组织
ELF文件参与程序的连接(建立一个程序)和程序的执行(运行一个程序),编译器和链接器将其视为节头表(section header table)描述的一些节(section)的集合,而加载器则将其视为程序头表(program header table)描述的段(segment)的集合,通常一个段可以包含多个节。可重定位文件都包含一个节头表,可执行文件都包含一个程序头表。共享文件两者都包含有。为此,ELF文件格式同时提供了两种看待文件内容的方式,反映了不同行为的不同要求。
从链接的角度看,ELF文件从开始到结束,可以看成是如下组成的:
a)ELF文件头
b)程序头表(可选)
c)第1节,第2节,…,第n节,…
d)节头表
从执行的角度看,ELF文件从开始到结束,可以看成是如下组成的:
a)ELF文件头
b)程序头表
c)第1段,第2段,…,
d)节头表(可选)

2、文件头(Elf header)
Elf头在程序的开始部位,作为引路表描述整个ELF的文件结构,其信息大致分为四部分:一是系统相关信息,二是目标文件类型,三是加载相关信息,四是链接相关信息。
其中系统相关信息包括elf文件魔数(标识elf文件),平台位数,数据编码方式,elf头部版本,硬件平台e_machine,目标文件版本 e_version,处理器特定标志e_ftags:这些信息的引入极大增强了elf文件的可移植性,使交叉编译成为可能。目标文件类型用e_type的值表示,可重定位文件为1,可执行文件为2,共享文件为3;加载相关信息有:程序进入点e_entry.程序头表偏移量e_phoff,elf头部长度 e_ehsize,程序头表中一个条目的长度e_phentsize,程序头表条目数目e_phnum;链接相关信息有:节头表偏移量e_shoff,节头表中一个条目的长度e_shentsize,节头表条目个数e_shnum ,节头表字符索引e shstmdx。可使用命令”readelf -h filename”来察看文件头的内容。

3、程序头表(program header table)
程序头表告诉系统如何建立一个进程映像.它是从加载执行的角度来看待elf文件.从它的角度看.elf文件被分成许多段,elf文件中的代码、链接信息和注释都以段的形式存放。每个段都在程序头表中有一个表项描述,包含以下属性:段的类型,段的驻留位置相对于文件开始处的偏移,段在内存中的首字节地址,段的物理地址,段在文件映像中的字节数.段在内存映像中的字节数,段在内存和文件中的对齐标记。可用”readelf -l filename”察看程序头表中的内容。

ELF文件的特点

1、平台相关
在ELF 文件头中包含了足够的平台相关信息,如数据编码方式,平台位数,硬件平台e_machine等,这些平台相关信息可在编译由编译器决定。

2、PIC
ELF可以生成一种特殊的代码——与位置无关的代码(position-independent code,PIC)。用户对gcc使用-fPIC指示GNU编译系统生成PIC代码。它是实现共享库或共享可执行代码的基础.这种代码的特殊性在于它可以加载到内存地址空间的任何地址执行.这也是加载器可以很方便的在进程中动态链接共享库。
PIC的实现运用了一个事实,就是代码段中任何指令和数据段中的任何变量之间的距离都是一个与代码段和数据段的绝对存储器位置无关的常量。因此,编译器在数据段开始的地方创建了一个表.叫做全局偏移量表(global offset table.GOT)。GOT包含每个被这个目标模块引用的全局数据目标的表目。编译器还为GOT中每个表目生成一个重定位记录。在加载时,动态链接器会重定位GOT中的每个表目,使得它包含正确的绝对地址。PIC代码在代码中实现通过GOT间接的引用每个全局变量,这样,代码中本来简单的数据引用就变得复杂,必须加入得到GOT适当表目内容的指令。对只读数据的引用也根据同样的道理,所以,加上 IC编译成的代码比一般的代码开销大。
如果一个elf可执行文件需要调用定义在共享库中的任何函数,那么它就有自己的GOT和PLT(procedure linkage table,过程链接表).这两个节之间的交互可以实现延迟绑定(lazy binging),这种方法将过程地址的绑定推迟到第一次调用该函数。为了实现延迟绑定,GOT的头三条表目是特殊的:GOT[0]包含.dynamic 段的地址,.dynamic段包含了动态链接器用来绑定过程地址的信息,比如符号的位置和重定位信息;GOT[1]包含动态链接器的标识;GOT[2]包含动态链接器的延迟绑定代码的入口点。GOT的其他表目为本模块要引用的一个全局变量或函数的地址。PLT是一个以16字节(32位平台中)表目的数组形式出现的代码序列。其中PLT[0]是一个特殊的表目,它跳转到动态链接器中执行;每个定义在共享库中并被本模块调用的函数在PLT中都有一个表目,从 PLT[1]开始.模块对函数的调用会转到相应PLT表目中执行,这些表目由三条指令构成。第一条指令是跳转到相应的GOT存储的地址值中.第二条指令把函数相应的ID压入栈中,第三条指令跳转到PLT[O]中调用动态链接器解析函数地址,并把函数真正地址存入相应的GOT表目中。被调用函数GOT相应表目中存储的最初地址为相应PLT表目中第二条指令的地址值,函数第一次被调用后.GOT表目中的值就为函数的真正地址。因此,第一次调用函数时开销比较大.但是其后的每次调用都只会花费一条指令和一个间接的存储器引用。

3、强大的工具支持
由于gnu有大量的工具支持elf文件格式.随着gnu工具的功能的扩展.程序员对ELF文件的运用也越来越灵活。例如,在C++中全局的构造函数和析构函数必须非常小心的处理碰到的语言规范问题。构造函数必须在main函数之前被调用。析构函数必须在main函数返回之后被调用。ELF文件格式中,定义了两个特殊的节 (section),.init和.fini,.init保存着可执行指令,它构成了进程的初始化代码。当一个程序开始运行时,在main函数被调用之前 (c语言称为main),系统安排执行这个section的中的代码。.fini保存着可执行指令,它构成了进程的终止代码。当一个程序正常退出时.系统安排执行这个section的中的代码。C++编译器利用这个特性.构造正确的.init和.fini sections.并结合.ctors(该section保存着程序的全局的构造函数的指针数组)和.dtors(该section保存着程序的全局的析构函数的指针数组)两个section,完成全局的构造函数和析构函数的处理。
GCC还有许多扩展的特性.有些对ELF 特别的有用。其中一个就是attribute 。使用attribute可以使一个函数放到CTOR_LIST或者DTOR_LIST里。 attribute((constructor))促使函数在进入main之前会被自动调用。attribute((destructor))促使函数在main返回或者exit调用之后被自动调用。这种函数必须是不能带参数的而且必须是static void类型的函数。在ELF下,这个特性在一般的可执行文件和共享库中都能很好的工作。另外一个GCC的特性是 attribute_(section(“sectionname”)),使用这个,能把一个函数或者是数据结构放到任何的section中。

编译器-static静态编译的好处以及工作内容

编译器的下一步工作,就是把外部函数的代码(通常是后缀名为.lib和.a的文件),添加到可执行文件中。这就叫做连接(linking)。这种通过拷贝,将外部函数库添加到可执行文件的方式,叫做静态连接(static linking),后文会提到还有动态连接(dynamic linking)。

静态编译时需要将所有的.a库链接到可执行程序中

可执行文件需调用.so文件方可正常运行,灵活但稍显麻烦;用静态链接库编译生成的可执行文件可直接运行,不用再调用如.so般的依赖库文件,简单但不灵活。

静态链接库流程:

1、编译生成目标文件

gcc -c struct.c

2、创建静态库

ar cqs libstruct.a struct.o (顺序不能乱)

3、链接静态链接库,生成可执行文件

gcc main.c -static -L. -lstruct -o main

静态库的链接方法:
gcc –o staticcode –L. –lstatic main.c –static(默认库在当前文件夹)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值