嵌入式开发中的 链接

本文探讨了嵌入式开发中链接地址对程序运行的影响。通过对比不同链接地址的程序,发现代码段地址的变化不会导致程序跑飞。但若程序使用了未初始化的全局变量,即bss段,则可能因地址不当引发错误。文章还提供了通过更改变量存储位置解决问题的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

嵌入式开发中的链接
这是一个一直困扰我的问题。
当一个程序脱离了操作系统,直接运行在裸的硬件之上时,那么你的链接地址就在背后左右着程序的运行。
首先,我遇到一个问题。当我使用别人的源码,编译,链接,然后下载运行,这一套的流程执行的很顺利,最终达到了想要的结果。而当我自己编写源码,然后用相同的流程,程序却总是跑飞了。
由于当时的知识和时间,都没有达到一定的水平,所以这个问题搁置了很长时间。近期要离职了,工作交接完毕,闲暇时间来解决一下自己很长时间以来的一个疑问。
最初的想法。我的程序编译完后,生成的地址相关码?意思就是如果我的程序在连接时,如果指定的代码段位置是错误的,那么下载到硬件中,如果不在对应的位置,就会出错。这是一个错误的想法。
前提。我知道当一个程序被编译连接完毕,那么生成的文件是一个elf格式的文件。如果有操作系统的话,内核中的elf格式handler 就会去解释elf的属性,把各个段的数据放到指定内存位置,然后执行。所以在操作系统上elf格式的文件,就可以被内核中的执行器加载执行。那么在没有操作系统的情况下,是如何执行的呢?通过objcopy -O binary 来完成。一个elf格式的文件在裸的硬件上,是无法运行的。实际上,能够直接运行的是二进制的代码。通过objcopy把elf中的text段,rodata,data,bss段提取出来,输出一个纯的二进制代码。这个二进制的代码,就是所有程序能够运行的核心。
关于地址相关的测试。对同一段代码,做编译。在链接时,使用不同的text段地址。如,arm-linux-gcc -Ttext=0x80004000/0x80003000。最后分别使用arm-linux-objcopy -dx exe。通过比较,发现只存在一个字节的不同,再查看汇编代码,发现这一个字节是一个填充字节,程序永远不会访问到该字节。这说明了一个问题,链接地址对代码段的影响有限,最终通过objcopy出来的二进制文件会忽略链接地址的不同。而且,编译器编译后的代码都是地址无关码,所有的关于地址的访问都是基于程序指针pc的偏移,所以对于这样一个代码,你下载到任何可以运行程序的介质和地址都可以顺利运行,不会跑飞。
我的疑问。既然代码是地址无关的,那我的程序为什么会产生错误呢?通过objdump自己查看我的程序跟别人程序的区别。我的程序,多了bss段。看来,问题就出在了bss段。仔细看看,反汇编的来的汇编程序,发现程序对bss段的引用都是基于绝对地址的。意思就是,bss段的引用并不是通过pc指针的,而是在链接时就计算好的。
链接过程和链接脚本。在gnu的编译器链接时,可以指定一个链接脚本。这个脚本指定了链接过程中各个段的链接位置。而且默认状况下,如果不指定链接脚本,编译器会有默认的链接地址和各个段的排布顺序。比如,.text/.rodata/.data/.bss。如果按照上述的段排布顺序,如果你的.text段的地址是0x80002000,那么.bss段就会按照顺序,排在.data段之后。比如,可能是0x80005000。所以对代码段链接地址的设定,就会间接影响.bss段的地址。
深层原因。此时,已经知道了,代码段地址对于.bss段的影响。意思就是,如果.text段的位置为0,那么.bss段的位置就会是0x5000。而程序在访问.bss段的数据时,就会导致异常,比如该位置是无效位置,或者是一个错误的数据。
解决方法。那么如何解决我的问题,使整个程序变成一个可以随意下载的程序呢?熟悉.bss段的都知道,.bss段用于存储没有初始化的全局变量。显然,我的程序使用了没有初始化的全局变量。所以,会有.bss段的存在。如果把全局变量,改成栈变量,就可以避免这个问题了。
其他测试。既然.bss段的存在,导致了问题的发生,那么其他段,比如.rodata,.data段又是怎么样呢?在程序中添加一行 char * a = "123123";编译后,反汇编,发现他们会跟.bss段产生同样的后果。
总结。gnu编译器允许自定义 一个section。但是对除代码段以外的其他段的访问,都是依赖于绝对地址。所以,访问了其他段的程序,在编译时必须注意段地址的有效性。要么,就写一个只有代码段的程序吧,它就是可以到处下载运行的程序了。
意外发现。通过把switch 换成if语句,可以有效节省代码段的空间。谁要是不信,可以自己做个程序测试看看。switch arm-linux-gcc时,会保留一段无用的空间,if时,就不会有这个浪费。条件分支在10个左右时,将节省120个字节左右的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值