1. 简介
BusyBox 是一个集成了一百多个最常用linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。
简单的说BusyBox就好像是个大工具箱,它集成压缩了Linux的许多工具和命令。除此之外,提供了良好的编程框架,用户能够将自己的命令集成到busybox当中。在实际的使用过程中,busybox常常被用于制作linux的根文件系统。
2. 静态编译
下载busybox源码,进行配置编译
curl http://busybox.net/downloads/busybox-1.23.2.tar.bz2 | tar xjf -
mkdir -p obj/busybox
cd busybox-1.23.2
make O=../obj/busybox defconfig #独立在新文件中进行相关配置
cd ../obj/busybox
make menuconfig
修改配置,使用静态编译,如果不使用静态编译,程序运行期间需要进行动态加载,则需在根文件系统中提供其所需的共享库。
Location:
-> Busybox Settings
-> Build Options
[*] Build BusyBox as a static binary (no shared libs)
使用make
进行编译,对于一些机器,可能会报如下的错误:
networking/lib.a(inetd.o): In function `unregister_rpc‘:
inetd.c:(.text.unregister_rpc+0x17): undefined reference to `pmap_unset‘
networking/lib.a(inetd.o): In function `register_rpc‘:
inetd.c:(.text.register_rpc+0x56): undefined reference to `pmap_unset‘
inetd.c:(.text.register_rpc+0x72): undefined reference to `pmap_set‘
networking/lib.a(inetd.o): In function `prepare_socket_fd‘:
inetd.c:(.text.prepare_socket_fd+0x7f): undefined reference to `bindresvport‘
collect2: ld returned 1 exit status
make[2]: *** [busybox_unstripped] Error 1
make[1]: *** [_all] Error 2
make: *** [all] Error 2
观察上面的错误,可以发现问题出在inetd.c
中有未定义的引用,在网上搜索一下答案,关闭配置当中的inet
选项即可忽略该问题
Location:
-> Networking Utilities
[ ] inetd
这时再执行make
,就能生成busybox
,执行make install
生成_install
目录,该目录中生成了常用的linux命令,这些命令均是符号链接到busybox上的。
3. 动态编译
网上大部分人都讲述的如何静态编译,而少部分人的讲述并未考虑到不同平台带来的问题,而我使用的平台为x86_64架构上,作为第一次探索动态编译,在这个过程当中遇到不少困难,希望下面的过程,能够对你有用。
接下来,我们会使用到以下几个命令:
- readelf:用来读ELF文件相关信息的命令
- ldd :用来打印共享链接库信息的命令
先来看看这两条命令有什么神奇之处:
#include <stdio.h>
#include <stdlib.h>
int main(){
printf("Hello World\n");
return 0;
}
gcc hello.c -o hello
readelf -d hello
Dynamic section at offset 0xe50 contains 20 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x4003c8
0x000000000000000d (FINI) 0x4005e8
… … …
相信,你已经发现Shared library
,共享库libc.so.6
正是该程序需要的动态链接库,那我们再接下来看下ldd
命令:
ldd hello
linux-vdso.so.1 => (0x00007ffc895e8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4a7e155000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4a7e526000)
这里的结果,更令人兴奋,因为它不仅告诉了所需的链接库,同时也交待它们的具体位置。(想当初,不晓得有这个命令,自己在/lib
目录下各种尝试,那才辛酸)
但这里的结果,想必也让你迷惑,为何这里出现了三个共享的链接库呢?对于/lib64/ld-linux-x86-64.so.2
比较好理解,这是程序动态链接所需要的链接器,那这个linux-vdso.so.1
是什么呢?
vdso: Virtual Dynamic Shared Object
虚拟动态共享库,是不是体会到什么了?来看看这段
在linux中,glibc是程序与内核之间的桥梁,如果内核增添的新的特性,带来了API的改变,这个时候如果glibc想要支持该新的特性,就需要对glibc进行升级。然而,glibc和linux不是一块开发的,同时glibc还要去兼容不同版本的linux内核,而linux内核也需要去兼容不同版本的glibc,因此这个虚拟共享库诞生,我感觉这个有点像一个虚拟框架,这个框架不会改变,glibc和linux内核彼此在这个框架下进行修改。
ldd
命令的-u
参数可以用来查看未使用的动态链接库,来看看有什么样的效果呢:
ldd -u hello
Unused direct dependencies:
linux-vdso.so.1
使用strace ./hello 来跟踪下hello的执行:
execve("./hello", ["./hello"], [/* 18 vars */]) = 0
brk(0) = 0x2373000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3200d55000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=62944, ...}) = 0
mmap(NULL, 62944, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f3200d45000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
其实只发现它打开了libc.so.6
。
到了现在我们开始对busybox进行动态编译,动态编译的方法很简单,去掉之前静态编译的选项,即可。
然后根据ldd显示的动态链接库地址,当运行在不同机器上时,在相关路径下,需存有所需的链接库。
/lib/x86_64-linux-gnu/libc.so.6
/lib64/ld-linux-x86-64.so.2
这一步,主要用于在制作根文件系统时,当运行在嵌入式设备上时,需要提供所需要的链接库。
参考资料
版权声明:本文为博主原创文章,未经博主允许不得转载。