嵌入式linux下如何混用多种C/C++运行时环境(Glibc和uClibc混用问题)
最近遇到一个问题,就是需要上新的硬件平台(SOC型号),但我们软件中某个组件的供应商不愿意为新的硬件平台编译SDK。旧的硬件平台是uClibc,新的SOC官方提供的运行时库为musl-libc。按理说我们是无法在新的程序中直接连接旧SDK的.a的,但这两个SOC的硬件指令集架构一致,都是armv7-a的,这就给我们提供了一个混用的可能性。
我们知道,对于linux操作系统来讲,其实只有进程概念,没有所谓glibc/uclibc等运行时库(合集)的概念,甚至就连所谓进程概念也是一步步发展起来的,可能最早只是一段代码,加载到内存后就直接跳转执行了。
那既然linux操作系统本身没有运行时的概念,那uclibc和musl-libc是为什么存在呢,它们在进程加载和执行过程中起了什么作用呢?
直接说结论吧,不同的c语言运行时,其实就是不同的“二次加载和执行方式”。
下图是uclibc和musl-libc的运行时库


可以看到,他们很多部分是相同的,如都有libc/libgcc_s/libstdc++等库。且都有一个ld-开头的so。
其实核心就是这个ld-开头的库,它意思是“loader & dynamic linker”,意味着它其实才是真正意义上的“程序入口”,我们在程序中编写的main函数,其实不会真正被当做入口执行,而是会被这个so进行调用。
这个大家可以在网络上搜索一下,ELF的标准中可执行程序可以包含PT_INTERP段,简单来说,如果包含了这个段,那么程序入口就会变成这个段提供的so而非当前的可执行程序文件。

我们使用不同编译链生成可执行程序时,它默认就帮我们指定了interpreter的全路径。

那么好,基于上述的知识,我们知道了程序的真实入口地址其实是PT_INTERP所在的库,它会去读取RPATH(或环境变量LD_LIBRARY_PATH)来加载其他库(例如libc和程序所需三方库)。
那么我们如果需要在新的SOC平台上把旧平台的uClibc运行时库整个复制过去,并改动PT_INTERP和RPATH路径,是否就可以了呢?

当然,如果可执行程序是我们自己编译的,还可以把这两个参数加入编译选项。上文中使用patchelf主要是提供给已经编译好的可执行程序使用

总结来说,想要在同一个运行着的操作系统中运行不同的C/C++运行时环境(例如glibc/uclibc混用)是完全可以实现的,只需要更改加载器和库搜索路径即可。
(当然,这样做出来的进程肯定是独立的,需要通过管道或本机套接字进行通信;而且库的目录也不要相同,因为可能会冲突,例如libc.so.0之类可能会在两个运行时环境中命名相同)
最后修改时间:2025-04-17 14:10:43

浙公网安备 33010602011771号