GNU工具链(下面简称 工具链)是一个包含了由GNU项目所产生的各种编程工具的集合。这些工具形成了一条工具链,用于开发应用程序和操作系 统。GNU工具链在针对LFS、嵌入式系统的Linux内核、BSD及其它软件的开发中起着至关重要的作用。
在LFS中,工具链是整个过程的核心,可以说,没有做好工具链就一定做不成一个完整的系统。工具链在这里是研究的主题,你可以通过认识工具链来深刻了解到Linux系统内部的依赖关系,从而对以后构建一个完整的Linux系统掌握的游刃有余。下面我们来逐步了解工具链的各个细节。
概览
1、工具链的组成结构
2、工具链的使用方式
3、工具链的制作方式
4、制作 "工具操作系统"
5、总结工具链的特点
第一、工具链的组成结构
GNU工具链的组件有:
- GNU make:用于编译和构建的自动工具;
- GNU编译器集(GCC):一组多种编程语言的编译器;
- GNU Binutils:包含链接器、汇编器和其它工具的工具集;
- GNU调试工具(GDB):代码调试工具;
- GNU自动化生成工具(autotools):自动化检查软件编译过程的工具。
前面简单介绍了工具链的相关概念与结构,为了对工具链实施完整的分析,本文选取LFS的工具链来说明工具链体系内的软件包组成及依赖关系。
所需的软件包有:
- Binutils:
- 包含的程序:addr2line,ar,as,c++filt,elfedit,gprof,ld,ld.bfd,nm,objcopy,objdump,ranlib,readelf,size,strings 和 strip
- 包含的库:libiberty,libbfd 和 libopcodes
- GCC:
- 包含的程序:c++,cc(到 gcc 的链接),cpp,g++,gcc,gcc-ar,gcc-nm,gcc-ranlib,gccbug 和 gcov
- 包含的库:libgcc,libgcov,libgomp,liblto_plugin,libmudflap,libquadmath,libssp,libstdc++,libsupc++
- 依赖的包:gmp,mpfr,mpc
- Linux API Headers:(这个是可选包,如果是为了做一个通用工具链,必须将其换成相应平台的头文件包)
- 包含的头文件:/usr/include/asm/*.h,/usr/include/asm-generic/*.h,/usr/include/drm/*.h,/usr/include/linux/*.h,/usr/include/mtd/*.h,/usr/include/rdma/*.h,/usr/include/scsi/*.h,
/usr/include/sound/*.h,/usr/include/video/*.h,/usr/include/xen/*.h
- 包含的头文件:/usr/include/asm/*.h,/usr/include/asm-generic/*.h,/usr/include/drm/*.h,/usr/include/linux/*.h,/usr/include/mtd/*.h,/usr/include/rdma/*.h,/usr/include/scsi/*.h,
- Glibc:(可以根据标准LFS系统的制作方法来安装Glic,这是个很有用的建议)
- 包含的程序:catchsegv,gencat,getconf,getent,iconv,iconvconfig,ldconfig,ldd,lddlibc4,locale, localedef,makedb,mtrace,nscd,pcprofiledump,pldd,pt_chown,rpcgen, sln, sotruss, sprof, tzselect, xtrace, zdump 和 zic
- 包含的库:ld.so,libBrokenLocale,libSegFault,libanl,libbsd-compat,libc,libcidn,libcrypt,libdl,libg,libieee,libm,libmcheck,libmemusage,libnsl,
libnss,libpcprofile,libpthread,libresolv,librpcsvc,librt,libthread_db,libutil
以下属于工具环境所要用到的包,与工具链无关。
- Ncurses:(与命令行相关的程序都会用到libncurses库)
- 包含的程序:captoinfo(到 tic 的链接), clear, infocmp, infotocap (到 tic 的链接), ncursesw5-config, reset (到 tset 的链接), tabs, tic, toe, tput 和 tset
- 包含的库:libcurses,libncurses,libform,libmenu,libpanel
- Bash:(bash是从宿主环境切换到工具环境要用到的,处理方式在制作过程中有相关阐述)
- 包含的程序:bash,bashbug 和 sh (到 bash 的链接)
- Coreutils:(Coreutils 程序包包含用于显示和设置基本系统属性的工具,属于工具环境的主要部分)
- 包含的程序:base64, basename, cat, chcon, chgrp, chmod, chown, chroot, cksum, comm, cp, csplit, cut, date, dd, df, dir, dircolors, dirname, du, echo, env, expand, expr, factor, false, fmt, fold, groups, head, hostid, id, install, join, link, ln, logname, ls, md5sum, mkdir, mkfifo, mknod, mktemp, mv, nice, nl, nohup, nproc, od, paste, pathchk, pinky, pr, printenv, printf, ptx, pwd, readlink, realpath, rm, rmdir, runcon, seq, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, shred, shuf, sleep, sort, split, stat, stdbuf, stty, sum, sync, tac, tail, tee, test, timeout, touch, tr, true, truncate, tsort, tty, uname, unexpand, uniq, unlink, users, vdir, wc, who, whoami 和 yes
- 包含的库:libstdbuf.so
- Diffutils:
- 包含的程序:cmp, diff, diff3 和 sdiff
- File:
- 包含的程序:file
- 包含的库:libmagic
- Findutils:
- 包含的程序:bigram, code, find, frcode, locate, oldfind, updatedb 和 xargs
- Gawk:
- 包含的程序:awk (到 gawk 的链接), dgawk, gawk, gawk-4.0.1, grcat, igawk, pgawk, pgawk-4.0.1 和 pwcat
- Gettext:(按照LFS手册的观点,工具环境中并不需要这个软件包太多的程序,事实上可以依据制作标准LFS系统的方法来安装)
- 包含的程序:autopoint, config.charset, config.rpath, envsubst, gettext, gettext.sh, gettextize, hostname, msgattrib, msgcat, msgcmp, msgcomm, msgconv, msgen, msgexec, msgfilter, msgfmt, msggrep, msginit, msgmerge, msgunfmt, msguniq, ngettext, recode-sr-latin 和 xgettext
- 包含的库:libasprintf,libgettextlib,libgettextpo,libgettextsrc,preloadable_libintl
- Grep:
- 包含的程序:egrep、fgrep 和 grep
- M4:
- 包含的程序:m4
- Make:
- 包含的程序:make
- Patch:
- 包含的程序:patch
- Perl:(FreeBSD系统中并不附带perl,可见perl并不是系统必须的部分,但是工具环境在编译某些包时需要用perl编译perl相关的模块)
- 包含的程序:a2p, c2ph, config_data, corelist, cpan, cpan2dist, cpanp cpanp-run-perl, enc2xs, find2perl, h2ph, h2xs instmodsh, json_pp, libnetcfg, perl, perl5.16.2 (到 perl 的链接), perlbug, perldoc, perlivp, perlthanks (到 perlbug 的链接), piconv, pl2pm, pod2html, pod2latex, pod2man, pod2text, pod2usage, podchecker, podselect, prove, psed (到 s2p 的链接), pstruct (到 c2ph 的链接), ptar, ptardiff, ptargrep, s2p, shasum, splain, xsubpp 和 zipdetails
- Sed:
- 包含的程序:sed
- Textinfo:
- 包含的程序:info、infokey、install-info、makeinfo、pdftexi2dvi、texi2dvi、texi2pdf 和 texindex
- Tar、Gzip、Bzip2、Xz:(LFS手册很像是按照字母顺序来编译的,没有发现顺序要求,推荐tar最后安装)
- 各自包含的程序:Tar { rmt,tar },Bzip2 { bunzip2 (到 bzip2 的链接),bzcat (到 bzip2 的链接),bzcmp (到 bzdiff 的链接),bzdiff,bzegrep (到 bzgrep 的链接),bzfgrep (到 bzgrep 的链接),bzgrep,bzip2,bzip2recover,bzless (到 bzmore 的链接) 和 bzmore },Gzip { gunzip, gzexe, gzip, uncompress, zcat, zcmp, zdiff, zegrep, zfgrep, zforce, zgrep, zless, zmore, and znew },Xz { lzcat (到 xz 的链接)、lzcmp (到 xzdiff 的链接)、lzdiff (到 xzdiff 的链接)、lzegrep (到 xzgrep 的链接)、lzfgrep (到 xzgrep 的链接)、lzgrep (到 xzgrep 的链接)、lzless (到
xzless 的链接)、lzma (到 xz 的链接)、lzmadec、lzmainfo、lzmore (到 xzmore 的链接)、unlzma (到 xz 的链接)、unxz (到 xz 的链接)、xz、xzcat (到 xz 的链接)、xzcmp (到 xzdiff 的链接)、xzdec、xzdiff、xzegrep (到 xzgrep 的链接)、xzfgrep (到 xzgrep 的链接)、xzgrep、xzless、xzmore } - 各自包含的库:Bzip2(libbz2*),Xz(liblzma*)
- 各自包含的程序:Tar { rmt,tar },Bzip2 { bunzip2 (到 bzip2 的链接),bzcat (到 bzip2 的链接),bzcmp (到 bzdiff 的链接),bzdiff,bzegrep (到 bzgrep 的链接),bzfgrep (到 bzgrep 的链接),bzgrep,bzip2,bzip2recover,bzless (到 bzmore 的链接) 和 bzmore },Gzip { gunzip, gzexe, gzip, uncompress, zcat, zcmp, zdiff, zegrep, zfgrep, zforce, zgrep, zless, zmore, and znew },Xz { lzcat (到 xz 的链接)、lzcmp (到 xzdiff 的链接)、lzdiff (到 xzdiff 的链接)、lzegrep (到 xzgrep 的链接)、lzfgrep (到 xzgrep 的链接)、lzgrep (到 xzgrep 的链接)、lzless (到
第二、工具链的使用方式
工具链的目的是提供一个临时可用的编译工作环境,通过chroot来完成在工具环境中进行开发、编译、制作工作。为了制作出干净、可移植的工具环境,建议创建一个专用于制作工具链的用户,这也是LFS推荐的。本文并不仅限于LFS的工具链的使用,仅以此为范例,如果你所使用的发行版有完整的工具链环境,你就不需要下面的代码了,只需要按照软件编译方法去做即可。
在使用工具链之前,你的身份应该是root,首先挂载虚拟文件系统,然后进入到chroot环境中。下面提供了LFS的脚本示例可以使过程更加简便。
挂载虚拟文件系统
#!/bin/bash mount -o bind /dev $LFS/dev mount -t devpts devpts $LFS/dev/pts -o gid=5,mode=620 mount -t proc proc $LFS/proc mount -t sysfs sysfs $LFS/sys if [ -h $LFS/dev/shm ]; then link=$(readlink $LFS/dev/shm) mkdir -p $LFS/$link mount -t tmpfs shm $LFS/$link unset link else mount -t tmpfs shm $LFS/dev/shm fi
进入到Chroot环境
#!/bin/bash chroot "$LFS" /tools/bin/env -i \ HOME=/root \ TERM="$TERM" \ PS1='\u:\w\$ ' \ PATH=/tools/bin:/tools/sbin:/bin:/usr/bin:/sbin:/usr/sbin \ /tools/bin/bash --login +h
将以上代码保存为相应的Shell脚本文件,添加执行权限即可使用,使用顺序是先挂载虚拟文件系统、后进入chroot环境。上述代码只适用于LFS的构建,可根据需求做适当的变量替换,原理和步骤是相同的。
在进入工具环境后,可供使用的命令在第一节已经给出了。解压软件包源码后,应该创建一个build目录,保持源码目录的干净,在出现问题后可以直接在build目录中分析而不至于破坏源代码。
第三、工具链的制作方式
在着手探讨工具链的制作之前,首先要明确一点,本文并不试图将LFS的工具链制作过程再现一遍,而是将制作工具链的步骤和经验叙述出来。因此,如果你想深入了解整个工具链的编译过程,请参考LFS和CLFS的文档。
为了制作一个完整、可用且可移植的工具环境,我们以第一节的分析为基础,先将工具链编译出来,然后用做出的工具链再编译其他的工具,使得编译出的工具不依赖于宿主环境。
首先,编译Binutils和GCC,接着编译Glibc,这样工具链就可用了,但是还没有摆脱对宿主环境的依赖,因此要用工具链再编译自身,然后工具链就可移植了。此时,工具链已经可用独立了,如果想做一个完整的工具链系统,则需要用这个工具链再编译出一套环境。
按照第二节的要求,应该提供一个新的用户,专门用来编译工具链,当然这只是建议,真正需要的是工具环境变量。编译过程中依靠的是 "./configure --preifx=" 格式来指定要将工具链安装到何处,这是核心部分,其余的是对组件的选择问题。当这三个包编译完成后,工具链就可以用来编译其他的软件包了,如果你不打算立即使用工具环境,那么你完全可以将一个工具链继续扩展成一个系统,而编译时工具链却依赖宿主环境的一些程序和库文件,你就应该考虑移植问题了。解决移植问题在第四节继续论述。
第四、制作 "工具操作系统"
工具操作系统,是我对LFS的一个认识,它确实像是一整套的工具,可以生产出工具的一种工具,虽然没有提供完整、易用的操作环境,但是却提供了完整的编译环境,LFS的目的实际上是使学习者去了解Linux系统的内部运行机理,所以我并不打算拿这样的一个范本来敷衍我的知识。我们所说的工具操作系统更类似于BLFS。
在做好工具链之后,我们开始按照第一节的编译顺序编译完所有的包,请不要将man手册页也安装进工具环境,这和LFS的临时工具环境的制作方法是相同的,然后我们开始对一些细节来品味一下,继续将其扩展下去。
如果你完全按照LFS去做工具环境的话,很不幸的告诉你,在你使用时你是无法联网的,也无法查看系统的各种信息,那么我们的扩展就必须开始了。
1、联网工具:
需要的软件包:iana-etc、dhcpcd、inetutils、wget、openssl、pcre
分析:iana-etc包含的文件有/etc/protocols和/etc/services,这两个文件是为以后的dhcpcd和inetutils包的使用奠定基础的。dhcpcd是用来为网卡分配IP的,这是必要的工作。inetutils包最好不要完全按照LFS的要求去做,不要在./configure后添加那些disable参数,那些工具还是要用到的。wget是用来下载源码的工具,依赖于openssl包,openssl依赖于可选的PCRE包。将这些包安装后,即可联网了。
2、基础工具:
需要的软件包:glic、ncurses、util-linux、nano、S-Lang
分析:这里必须要将ncurses重点说一下,在编译glibc和ncurses时要按照编译LFS系统的方法去做,将Glibc的locale安装好,并且使ncurses支持宽字符,这样你就可以看到中文字符了,nano也依赖于ncurses的宽字符支持,S-Lang是nano的一个依赖包。nano是我选择代替vim的文本编辑器,事实上比vim更容易上手,也可以处理使用时的编辑工作。util-linux是工具环境中用来管理系统的一个包,在某些情况下,你可以在工具环境中来处理紧急问题,而不需要离开工具环境去解决问题,你也可以用一些常用的命令查看系统的当前信息,可以对你的编译工作提供帮助。
3、Shell工具:
可选的软件包:bash、csh、ksh、zsh、readline
分析:在编译工具链之后我们就开始编译bash了,那其实是Linux平台常用的Shell程序,但是在BSD平台上常用的是csh和ksh,此处不作介绍,重点是sh指向的地方是这几个Shell其中的一个,你完全可以用sh符号链接和/etc/passwd文件来指定Shell。readline为bash提供了命令行编辑库,
4、开发工具:
需要的软件包:libtool
分析:libtool是一个通用库支持脚本,将使用链接库的复杂性封装为统一、可移植的接口。提供的程序有libtool、libtoolize。
现在,一个可联网的、可日常用的工具环境已经做出来了,离我们的"工具操作系统"还有多远呢?还有一步之遥。我想,现在这个工具环境应该可以自给自足了吧,那么这个工具环境就是一个操作系统吗?我想,这个答案应该由读者自己回答,我只提示一点信息。
如果你是将工具环境放在一个独立的分区中的,并且假设你是一个非常有经验的Linux用户,你会发现工具环境的目录很像/usr目录里的结构,其实你完全可以使用Slackware提供的内核来使你的工具环境运作起来,你需要安装grub和os-prober,然后按照Slackware的BSD启动风格自己写rc脚本,这样就可以完成一个"工具操作系统"了,虽然并不能将其作为一个有特色的发行版发布出去,但它可以胜任你对工具环境的要求,你还可以用tar程序将整个工具环境打包出来,就成了一个可移动的工具链。现在还谈不上可移植,因为BSD、Windows、Mac OS X并不能执行Linux下编译的程序,但是至少可以在各种Linux发行版之间通用了,这是可移植的折中说法,我并不是有意去歪曲概念。
一个操作系统所具备的基础功能,它已经具备了,剩下的只是锦上添花的工作,也许以上的分析并不能完全使这个系统启动起来,对于一种学习上的深究来说,这才只是学习的起点,我们需要去做的还有很多。
第五、总结工具链的特点
虽然整篇文章一直在围绕LFS来谈工具链,但是我们不难发现,其实工具链最重要的部分是glibc库和binutils,Glibc是一个很完备的库环境,binutils提供了配合编译工具使用的工具套件,这些都可以由目标平台来提供,有了编译套件和库环境就可以编译其他工具了,这才是核心思想。如果要做出一个可用的工具环境,那么你还是要依赖宿主环境的,这也与你的开发目标有关,在使用时请尽量不要脱离宿主环境,一旦出现了问题就可以通过宿主环境来寻找解决的办法,这是基本策略。
在工具链技术上,并没有固定的平台与软件包,取决于你对工具的熟悉程度。如果每个人都想投机取巧地去设法躲过学习新的知识而完成现有的工作,那么以后的bug就会成为你痛苦的源泉。工具链本身已经可以满足你的开发要求了,但是做出一个工具环境可以使你日后的开发工作更加顺利,因为你可以将bug控制在掌握的范围内,这是工具链技术的一个很重要的优点。
当我们寻求解决各种error信息的解决方法时,干净的工具链环境使得我们可以快速抓住发现bug的那一瞬间,解决问题更为准确而直观,这是值得我们继续探索的目标。
参考:
Linux From Scratch http://cloud.github.com/downloads/davidgao/LFSCN/LFS-BOOK.html
GNU toolchain http://zh.wikipedia.org/wiki/GNU_toolchain
工具链技术说明 http://www.ha97.com/book/lfs-book-6.6/chapter05/toolchaintechnotes.html
工具链解析 http://blog.163.com/warking_xp/blog/static/103910320092310035741/
使用 GNU Libtool 创建库 http://www.ibm.com/developerworks/cn/aix/library/1007_wuxh_libtool/