您的位置:首页 > 其它

busybox静态编译及动态编译实践

2015-08-28 18:41 344 查看

1. 简介

  BusyBoxBusyBox 是一个集成了一百多个最常用linux命令和工具的软件。BusyBox 包含了一些简单的工具,例如ls、cat和echo等等,还包含了一些更大、更复杂的工具,例grep、find、mount以及telnet。有些人将 BusyBox 称为 Linux 工具里的瑞士军刀。

  简单的说BusyBox就好像是个大工具箱,它集成压缩了Linux的许多工具和命令。除此之外,提供了良好的编程框架,用户能够将自己的命令集成到busybox当中。在实际的使用过程中,busybox常常被用于***linux的根文件系统。

2. 静态编译

下载busybox源码,进行配置编译

[code]    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


  修改配置,使用静态编译,如果不使用静态编译,程序运行期间需要进行动态加载,则需在根文件系统中提供其所需的共享库。

[code]Location:                                                           
-> Busybox Settings                                        
   -> Build Options     
      [*] Build BusyBox as a static binary (no shared libs)


使用
make
进行编译,对于一些机器,可能会报如下的错误:

[code]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
选项即可忽略该问题

[code]    Location:                 
    -> Networking Utilities 
       [ ] inetd


  这时再执行
make
,就能生成
busybox
,执行
make install
生成
_install
目录,该目录中生成了常用的linux命令,这些命令均是符号链接到busybox上的。

3. 动态编译

  网上大部分人都讲述的如何静态编译,而少部分人的讲述并未考虑到不同平台带来的问题,而我使用的平台为x86_64架构上,作为第一次探索动态编译,在这个过程当中遇到不少困难,希望下面的过程,能够对你有用。

接下来,我们会使用到以下几个命令:

readelfreadelf:用来读ELF文件相关信息的命令

lddldd  :用来打印共享链接库信息的命令

先来看看这两条命令有什么神奇之处:

[code]#include <stdio.h>
#include <stdlib.h>
int main(){
        printf("Hello World\n");
        return 0;
}


gcc hello.c gcc\ \ \ hello.c\ \ \ -o helloo\ \ \ hello

readelf readelf\ \ -d hellod\ \ \ 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 helloldd \ \ \ 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 ldd \ \ \ -u hellou\ \ \ hello

 Unused direct dependencies:

    linux-vdso.so.1

使用strace ./hellostrace\ \ ./hello 来跟踪下hello的执行:

[code]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显示的动态链接库地址,当运行在不同机器上时,在相关路径下,需存有所需的链接库。

[code]/lib/x86_64-linux-gnu/libc.so.6 
/lib64/ld-linux-x86-64.so.2


这一步,主要用于在***根文件系统时,当运行在嵌入式设备上时,需要提供所需要的链接库。

参考资料

静态库和动态库

Linux内核特性之VDSO

linux下so动态库一些不为人知的秘密
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: