静态链接TvT

文章详细介绍了将两个目标文件a.o和b.o通过链接器合并成可执行文件ab的过程,包括空间分配策略、两步链接方法以及重定位。链接器首先合并相同性质的段,然后进行符号解析和重定位,最后形成完整的程序。通过objdump工具展示了链接前后地址分配的变化。
摘要由CSDN通过智能技术生成

                当有两个目标文件时,如何将它们链接起来形成一个可执行文件?假设我们使用下面图1的2个源文件代码a.c和b.c作为例子分析。

图1

                首先我们使用gcc将"a.c"和"b.c"分别编译成目标文件"a.o"和"b.o"(gcc -c a.c b.c)。

                经过编译以后我们得到了“a.o”和"b.o"这两个目标文件。从代码中可以看到,“b.c”总共定义了两个全局符号,一个是变量"shared",另外一个是函数"swap";"a.c"里面定义了一个全局符号就是"main"。模块"a.c"里面引用了"b.c"里面的"swap"和"shared"。我们接下来就要做的就是把"a.o"和"b.o"这两个目标文件链接在一起并最终形成一个可执行文件"ab"。

空间与地址分配

                对于连接器来说,整个链接过程中,它就是将几个输入目标文件加工后合并成一个输出文件。那么在这个例子里,我们的输入就是目标文件"a.o"和"b.o",输出就是可执行文件"ab"。我们知道,可执行文件中的代码段和数据段都是由输入的目标文件合并而来的。那么我们链接过程就很明显产生了第一个问题:对于多个输入目标文件,连接器如何将它们的各个段合并到输出文件?或者说,输出文件中的空间如何分配给输入文件。

        按序叠加

                一个简单的方案就是将输入的目标文件按照次序叠加起来,如图2所示。

图2

                 图2的做法很简单,就是直接将各个目标文件依次合并。但是这样做会造成一个问题,在有很多输入文件的情况下,输出文件将会由很多零散的段。比如一个规模稍大的应用程序可能会有数百个目标文件,如果每个目标文件都分别有.text段,.data段和.bss段,那最后的输出文件将会由成百上千个零散的段。这种做法非常浪费空间,因为每个段都需要有一定的地址和空间对齐要求,比如对于x86的硬件来说,段的装载地址和空间的对齐单位是也,也就是4096字节。那么就是说如果一个段的长度只有1个字节,它也要在内存中占用4096字节。这样会造成内存空间大量的内部碎片,这并不是一个很好的方案。

        相似段合并

                一个更实际的方法是将相同性质的段合并到一起,比如讲所有输入文件的".text"合并到输出文件的".text"段,接着是".data"段,".bss"段,如图3所示。

图3

                 现在连接器空间分配的策略基本上都采用上述方法中的第二种,使用这种方法的链接器一般都采用一种叫做两步链接的方法。也就是说整个链接过程分两步。

                第一步  空间与地址分配  扫描所有的输入目标文件,获得它们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,链接器将能够获得所有输入目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系。

                第二步 符号解析与重定位 使用上面第一步中收集到的所有信息,读取输入文件中段的数据,重定位信息,并且进行符号解析与重定位,调整代码中的地址等。事实上第二步是链接过程的核心,特别是重定位过程。

                我们使用ld连接器将"a.o"和"b.o"链接起来:

                ld a.o b.o -e main -o ab

                -e main 表示将main函数作为程序入口,ld连接器默认的程序入口为_start。

                -o ab 表示链接输出文件名为 ab,默认为a.out。

                让我们使用objdump来查看链接前后地址分配情况,如图4所示。

                图4

                 从图4可以看到链接前后各个段的熟悉,VMA表示Virtual Memory Address,即虚拟地址,LMA表示Load Memory Address,即加载地址,正常情况下这两个值应该是一样的。

                链接前后的程序中所使用的地址程序在进程中的虚拟地址,即我们关心上面各个段中的VMA和Size,而忽略文件偏移。我们可以看到,在链接之前,目标文件中的所有段的VMA都是0,因为虚拟空间还没有被分配,所以它们默认都为0。等到链接之后,可执行文件"ab"中的各个段都被分配到了相应的虚拟地址。这里的输出程序"ab"中,".text"段被分配到了地址0x400e8,大小0x71字节;".data"段从地址0x6001b8,大小为4字节。整个链接过程前后,目标文件各段的分配,程序虚拟地址如图5所示。

               图5

                在图5中,可以看到"a.o" 和"b.o"的代码段被先后叠加起来,合并成了"ab"的一个.text段,加起来长度为0x71,所以"ab"的代码段里面肯定包含了main函数和swap函数的指令代码。

图6 

                使用objdump命令看到地址0x6001b8所得内容是0x64,对照图1可以看到变量shared的值等100,能相互对的上。

时变阈值跟踪算法(Time-Varying Threshold Tracking Algorithm,TVT算法)是一种用于信号处理和检测的算法。该算法通过动态调整阈值来实现对信号的准确检测和跟踪。 在传统的固定阈值检测算法中,阈值是事先设定好的固定值。然而,在一些实际应用场景中,信号的特性可能会随时间发生变化,这时固定阈值检测算法就无法满足需求。TVT算法通过不断地根据信号的动态特性调整阈值,能够更准确地检测和跟踪信号。 TVT算法的基本思想是根据信号的历史观测值计算出一个动态的阈值,然后将当前观测值与该阈值进行比较,以确定信号是否满足检测条件。具体而言,TVT算法可以分为两个步骤:阈值更新和信号检测。 在阈值更新步骤中,根据信号的历史观测值计算出一个新的阈值。常用的方法包括指数加权移动平均法、卡尔曼滤波法等。这些方法能够利用过去观测值的信息来估计信号的动态特性,并相应地更新阈值。 在信号检测步骤中,将当前观测值与更新后的阈值进行比较。如果当前观测值超过阈值,则认为信号存在;否则认为信号不存在。通过不断地进行阈值更新和信号检测,TVT算法能够准确地跟踪信号的变化。 TVT算法在实际应用中具有广泛的应用,例如无线通信中的信号检测、目标跟踪、异常检测等领域。它能够适应信号特性的变化,提高检测和跟踪的准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值