
假设当前开发环境是x86/Debian,目标系统是一个3.0.x内核的ARM版Linux,后者用的是uClibc。现在希望编译出静态链接的ARM版gdb、gdbserver。
有人说只要ARM版gdbserver即可,多弄一个ARM版gdb自有我的道理。有人说,gdb应该是x86版的,只不过configure时target指定成ARM,我知道这种意图所在,但那不是本文关注点,我也不打算在x86版gdb中"target remote ..."。事实上我有与ARM版gdbserver相配合的x86工具IDA,我也有另一个ARM/Debian跑原生gdb。不再废话。
常见的两种交叉编译工具:
arm-none-eabi-gcc
arm-none-linux-gnueabi-gcc
前者一般是用于编译单片机固件,osmocom-bb项目、EZ-Connect Lite SDK等等用的是前者。后者是全功能的,头文件带得整齐。如果target是Linux而非单片机,必须用后者。
交叉编译工具链一般不是OS原生自带的。绝大多数人不需要自己下载gcc源码并编译生成相应的交叉编译工具链,我也只干过一次,折腾osmocom-bb项目时,自己编译生成arm-none-eabi-gcc 4.8.2。一般来说,放狗,下载别人提供的二进制包,这种包无所谓安装,解压后放在任何路径,使用前用"export PATH=$PATH:<path>"设置一下环境变量,确保arm-...-gcc在PATH中即可。可以说交叉编译工具链是一种100%纯绿色软件。我在x86/Debian上装了很多套不同的交叉编译工具链,不修改/etc/profile之类的,需要用哪个了就在当前shell里临时设一下PATH。
回到原始目标,看看我干过的变态事情:

arm-xxx-linux-gcc、arm-yyy-linux-gcc是目标系统配套的编译工具,尽管目标系统没有公开告诉我这点,但我还是设法识别出并弄到了它们。一个用uClibc,另一个用glibc,它们最多只能处理gdb-7.5。
arm-none-linux-gnueabi-gcc 4.6.1可以处理gdb-7.12,无法处理gdb-8.2。
arm-none-linux-gnueabi-gcc 4.8.3幺蛾子最少,可以处理此刻最新的gdb-8.2,不需要动任何其他手脚就可以顺利编译完成。
arm-...-gcc的版本不能太高。arm-linux-gnueabi-gcc 8.2.0是Debian 9原装货,它的glibc只能向下兼容到3.2.0版kernel,用它编译出来的静态链接的ARM版gdb在前述目标系统上运行时会报错:
FATAL: kernel too old
Aborted
为了交叉编译gdb,必须提前编译、安装ncurses库,5.9、6.1版都可以,能用新的就用新的。如果target是x86,一般有现成的ncurses库,target是ARM时很可能需要自己编译、安装ncurses库。
[scz@ /tmp]> arm-none-linux-gnueabi-gcc --version
arm-none-linux-gnueabi-gcc (Sourcery CodeBench Lite 2014.05-29) 4.8.3 20140320 (prerelease)
[scz@ /tmp]> arm-none-linux-gnueabi-gcc -print-search-dirs
这将显示arm-none-linux-gnueabi-gcc的搜索路径,比如从哪里找库文件,后面会用到。
http://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz
[scz@ /tmp]> mkdir ncurses
[scz@ /tmp]> cd ncurses
[scz@ /tmp/ncurses]> tar xfz /tmp/ncurses-6.1.tar.gz
[scz@ /tmp/ncurses/ncurses-6.1]> mkdir build-glibc
[scz@ /tmp/ncurses/ncurses-6.1/build-glibc]>
../configure --host=arm-none-linux-gnueabi
make
[scz@ /tmp/ncurses/ncurses-6.1/build-glibc]> ls -l lib
108608 Sep 28 16:33 libform.a
644854 Sep 28 16:33 libform_g.a
58042 Sep 28 16:33 libmenu.a
358612 Sep 28 16:33 libmenu_g.a
532756 Sep 28 16:33 libncurses.a
3017858 Sep 28 16:33 libncurses_g.a
25056 Sep 28 16:33 libpanel.a
224128 Sep 28 16:33 libpanel_g.a
把lib子目录下的一堆静态库(.a)复制到arm-none-linux-gnueabi-gcc的搜索路径中去。不需要复制头文件。
[scz@ /tmp/ncurses/ncurses-6.1/build-glibc]> cp lib/lib* <path>
如果之前没有编译安装ncurses库,编译gdb时可能会碰上错误提示:
configure: error: no termcap library found
http://ftp.gnu.org/gnu/gdb/gdb-8.2.tar.xz
[scz@ /tmp]> mkdir gdb
[scz@ /tmp]> cd gdb
[scz@ /tmp/gdb]> xz -cd /tmp/gdb-8.2.tar.xz | tar xf -
[scz@ /tmp/gdb]> cd gdb-8.2
[scz@ /tmp/gdb/gdb-8.2]> mkdir build-glibc
[scz@ /tmp/gdb/gdb-8.2]> cd build-glibc
../configure --without-python --disable-tui --host=arm-none-linux-gnueabi LDFLAGS="-static"
make
arm-none-linux-gnueabi-strip gdb/gdb gdb/gdbserver/gdbserver
ls -l gdb/gdb gdb/gdbserver/gdbserver
file -b gdb/gdb gdb/gdbserver/gdbserver
它的glibc向下兼容到2.6.16版kernel,在3.0.x版kernel的target上运行绰绰有余。
我在编译ncurses、gdb时都提前创建了一个名为build-glibc的子目录,然后在其中configure、make。创建这个子目录不是必须的,只不过洁癖使然,像我这样干,很容易一删了之,不污染父目录。为此还曾在Solaris上惹过麻烦,有兴趣者参看:
《自编译GDB执行出错后的排查》
http://scz.617.cn:8/unix/201603011322.txt
编译gdb时我在configure的未尾放了LDFLAGS="-static",可以不放这里,而是放在make的未尾,即:
make LDFLAGS="-static"
这个没有优劣对错,可以理解成vi与emacs、行尾{与行首{、goto与while(1)/break等等,你懂的。
说个不相关的,经常用tar,没想到这还是个有效单词,沥青、柏油。我第一次在计算机领域之外看到它,想当然地以为是要打包啥啥。据说*nix程序员们经常用到的命令还有:
$ unzip; strip; touch; grep; finger; mount; fsck; more; yes; umount; sleep




