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

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

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


参考资料

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-06 23:37:16

busybox静态编译及动态编译实践的相关文章

VC , Linux 静态编译与动态编译 (MD, MT)

首先从Linux下开始讲起,因为Linux编程对程序的理解要清楚一些,相比之下VC就比较容易糊涂. 当动态编译时,你发布的程序体积较小,在运行的时候需要同时提供你用到的dll / so文件. 当静态编译时,你发布的程序体积较大,包含所有符号,运行时不需要其他的 dll / so的支持,可以独立运行. 静态编译的目的是使发布的程序可以独立运行,不依赖于其他*.so,在技术上是把所有依赖的符号打包链接进了目标程序.这时候你会发现编译出来的程序的体积要大一些(Link的速度也会慢一些). 编译过程分为

apache 静态编译和动态编译参考

apache-2.2.22 编译安装笔记 一.静态编译     在使用./configure 编译的时候,即没有使用--enable-mods-shared=[module]或者--enable-[module]=shared这2个中的一个,那么所有的默认模块为静态.何谓静态? 其实就是编译的时候所有的模块自己编译进httpd 这个文件中,启动的时候这些模块就已经加载进来了,也就是可以使用了,通常:<ifmodule> </ifmodule> 来配置.所以大家看到的配置都是<

浅谈VB.Net 程序的编译和动态编译

---恢复内容开始--- 一般,我们都是通过Visual Studio(下面简称vs)来编写和编译vb.net应用程序的,但是,不少的人并不知道vs是通过何种方式编译程序的.今天,我们就来探讨一下编译vb.net程序的真正原理. 这篇随笔包含如下几个部分: 1.VS是怎么编译的 2.通过vbc.exe来编译应用程序 3.在代码中通过VBCodeProvider动态编译应用程序 ok,首先来说说vs编译应用程序的方法.其实,vs是通过调用vbc.exe来编译vbnet应用程序的.vs把用户编写的代

.Net Core Razor 预编译,动态编译,混合编译

预编译 预编译是ASP .Net Core的默认方式.在发布时,默认会将系统中的所有Razor视图进行预编译.编译好的视图DLL统一命名为 xxx.PrecompiledViews.dll 或者 xxx.Views.dll 动态编译 将项目整个配置成动态编译很简单,添加一个配置项目MvcRazorCompileOnPublish,值为false即可 <PropertyGroup> <MvcRazorCompileOnPublish>false</MvcRazorCompile

Apache静态编译与动态编译区别

静态编译: 在编译时,已经把相关模块编译进httpd二进制文件中不用再 httpd.conf 中在 LoadModule 来加载,只要在  <ifmodule></ifmodule> 中来配置就可以了. 动态编译: 编译的时候,使用 enable-module=shared  或者 enable-modules-shared=module 来动态编译. 动态显然就不编译到httpd里面去了,启动的时候根本不会加载这个模块, 而是给你一个 module.so  文件.你想用,就在ht

Apache静态编译与动态编译的区别

Apache拥有4层结构,从核心到外层的module.而外层的module可以用通过静态和动态两种方式与Apache共同工作.这也就引入下文的"动态"和"静态"两种编译安装方式. 静态编译: 编译的时候,所有的模块自己编译进  httpd  这个文件中,启动Apache的时候这些模块就已经加载进来了,只要在  <ifmodule></ifmodule>中来配置就可以了. 动态编译: 编译的时候,使用--enable-mods-shared=M

Apache静态编译与动态编译详解

Apache拥有4层结构,从核心到外层的module.而外层的module可以用通过静态和动态两种方式与Apache共同工作.这也就引入下文的“动态”和“静态”两种编译安装方式: 静态编译: 编译的时候,所有的模块自己编译进 httpd 这个文件中 ,启动Apache的时候这些模块就已经加载进来了,可以直接来使用,而不用再 httpd.conf 中在 LoadModule 来加载,只要在 <ifmodule></ifmodule> 中来配置就可以了. 动态编译: 编译的时候,使用e

静态编译、动态编译、静态链接库和动态链接库理解

1.静态编译:编译器在编译可执行文件时,把需要用到的对应动态链接库(.so或.ilb)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库. 2.动态编译: 动态编译的可执行文件需要附带一个的动态链接库,在执行时,需要调用其对应动态链接库中的命令.所以其优点一 方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源.缺点一是哪怕是很简单的程序,只用到了链接 库中的一两条命令,也需要附带一个相对庞大的链接库:二是如果其他计算机上没有安装对应的运行库,

静态编译和动态编译的区别【转】

本文转载自:https://segmentfault.com/a/1190000000580216 静态函数库一般扩展名为(.a),这类的函数库通常扩展名为libxxx.a .这类函数库在编译的时候会直接整合到程序中,所以利用静态函数库编译成的文件会比较大,这类函数库最大的优点就是编译成功的可执行文件可以独立运行,而不再需要向外部要求读取函数哭的内容:但是从升级难易度来看明显没有优势,如果函数库更新,需要重新编译. 动态函数库动态函数库的扩展名一般为(.so),这类函数库通常名为libxxx.s