2

交叉编译一个Sparc平台的小工具

 2 years ago
source link: https://blog.sbw.so/u/sparc32-gcc-cross-compile-for-router.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

交叉编译一个Sparc平台的小工具

来源: sbw Blog | 浏览: 250 | 评论: 2 发表时间: 2021-10-11

前几天,一个朋友问我能不能帮忙把一个自己写的小程序编译到一个Sparc平台的小型路由器上运行。当时我一听这个需求,感觉应该是没什么难度的,只需要对好目标平台环境,然后交叉编译即可。但在和他合作过程中发现,由于目标平台是经过魔改的,就导致各种编译不过。

首先,想要编译一个在某个平台的可执行程序,最好用的方法当然是在对应平台上直接使用编译器进行编译。当然,像这受限于目标平台的性能或资源,显然不可能在一个很弱的路由器上进行编译,所以只能选择交叉编译。想要交叉编译成功,首先肯定是要有一个能编译出目标平台指令的编译器,像GCC就提供了Sparc平台的支持,并且如何编译GCC为交叉编译器的文章在网上也很容易找到。

经过简单的搜索,我就在Github上找到了这个项目,它提供了一个帮助编译GCC交叉编译器的Python脚本。

具体的用法可以参考他的代码和文档,不过对于我的需求来说,我是需要让GCC同时支持C和C++语言的,而这个脚本默认只编译了C语言前端,所以需要修改脚本,在build_gcc函数中将--enable-languages=c改为--enable-languages=c,c++,添加对C++语言的支持。在脚本编译过程中,会自动从GNU网站上下载最新版本的GCC、binutils、gdb等依赖包,此处由于众所周知的网络问题,一般下载是会失败或者速度很慢的。所以也可以手动下载后,放在脚本同目录下,再执行。由于Sparc和x86一样,也是同时可以支持32和64位共存的系统,所以我索性将Sparc32和Sparc64两个平台的交叉编译器都编译出来了。

首先要验证编译出来的交叉编译器是否正常,所以写一个不包含任何头文件和有效语句,只包含main函数和一条返回语句的空的C语言程序进行编译。这样做的目的是,编译出一个不依赖任何库——甚至是libc库的二进制程序,看看是否能正常被操作系统加载并执行。但即使是一个空白的C程序,想要编译出来也是需要几个基本的依赖的,即CRT。在交叉编译时,使用的CRT要和目标平台一致。所以在编译交叉编译器时,默认就加了--disable-multilib参数,使得不会生成新的CRT库。直接编译的话,编译器会报错找不到相应的crt*.o库或一些像start、begin这样的函数。

但是,由于目标平台是一台经过高度定制化过的路由器,并且只提供一个不可交互的伪终端供远程访问和一个网页版本的文件管理器,所以目标平台的各种库版本都很难百分百确定。一切编译环境都要从头建立,从路由器中找到了所有类似于crti.o、crtn.o、crt1.o名称的几个文件,筛选出Sparc32的版本,然后加入gcc编译的静态链接库参数中,试了找到的好几个库的各种组合,终于可以正常编译过了。不过程序还是启动不起来——由于ld不一致。

LD是一个动态的程序链接器/加载器,用于控制一个程序的依赖解析和帮助程序启动。GCC默认的LD路径和目标平台实际的LD路径不一致,所以程序无法正常启动。在路由器中找到对应的LD之后,通过--dynamic-linker=指定LD位置,再次运行就能正常跑起来了。

然后再找到glibc.so,添加到GCC的动态链接库列表中,编译一个带printf输出的小程序,验证printf功能正常。当然,此时是没有任何头文件的,所以这里的printf是我自己声明的,但要和C的声明保持一致,这样才能正常链接。

然后就是很枯燥的在路由器上找所需要的头文件,由于目标平台是Sparc32和Sparc64共存的,所以相同头文件能找到很多个,有些是GCC提供的,有些是系统提供的。并且还需要分析找的的文件是用于Sparc32的还是Sparc64的。

找到所需要的头文件后,就不需要自己声明函数原型了,include找到的stdio.h头文件重新编译,能正常编译通过并执行正常,再试试libc中的其它函数,也可以使用。

到此,一个基本的交叉编译环境就算配置完成了。再使用这个环境去编译需要的底层依赖库,再编译需要的工具软件,自此就可以完成任何程序的编译了。

这个方法能成功的主要原因还是目标平台上保留了crt*.o和头文件这些东西,如果连这些文件也没有,那就很难真正编译出来了。不过,还是可以选择musl这样的独立libc库编译出不依赖原系统任何库的可执行程序,只是这样二进制的体积和加载后的内存占用肯定是没有这种“本地”编译好的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK