2014-04-19 20:54:02
構建Linux 編譯Linux BeagleBone Black
在簡書我還有個《構建Linux筆記v1.1》,
雖然這篇發布時間靠后,但卻是先寫的。
沒什么教學性質,就是個筆記,沒舍得刪。
這2個筆記都是 2014 年的。
由于太長了,分成了2部分:
構建Linux筆記v1.0(第二部分)
僅供參考,我用Linux的時間較少。
以下很多都是回憶,不一定準了。
我構建的目標平臺為BeagleBone Black。
交叉編譯工具為Sourcery CodeBench Lite。
要構建Linux,很簡單,只有3個部分,
- bootloader,即啟動引導程序,我選擇U-Boot。
- Linux內核。
- 應用程序。
我還沒研究是什么決定了32位和64位。
我主要的資料:
Running Linux
介紹Linux的書,這才叫入門級,了解下Linux的思想即可,書的內容可能有些過時,
這本書不是介紹怎么使用Linux的。如果想知道怎么使用Linux,
可看看Linux Pocket Guide,很薄。
鳥哥的書也不錯,感覺挺詳細的,好于大部分書,我沒看。
我連shell腳本都不會寫呢,很多命令沒用過,這與構建Linux關系不大。
Building Embedded Linux Systems
教構建嵌入式Linux的,了解下思想即可。
http://www.linuxfromscratch.org/
教構建各種Linux的網站,最關鍵的是網站給出了Linux由哪些應用程序構成。
我沒有用網站中的方式,sed那種命令不會。也是了解下思想即可。
If you can’t explain it simply, you don’t understand it well enough.
--Albert Einstein
準備SD卡
BeagleBone Black有4種啟動方式,我選擇用SD卡。
準備SD卡,將SD卡插到電腦中,在我的系統中被識別為/dev/sdb。
分區:
# fdisk /dev/sdb
用法很簡單,輸入m
,就可得到幫助。
第一個分區64M,類型為FAT32 (LBA),加上啟動標記。
第二個分區為余下的全部,類型默認為Linux,不必改。
參考過程是這樣的,
輸入o
,新建一個空DOS分區表,這會清除所有分區。
輸入p
,會列出分區列表,此時應該沒有。
輸入n
來添加新分區,直接enter
,接受默認為主分區,enter
接受默認為第一分區,
enter
接受默認的開始扇區,輸入+64M
,設置分區大小為64M。
輸入t
,來改變分區類型,自動選擇了第一個分區,輸入l可列出所有分區列表的代碼,
輸入c
選擇W95 FAT32(LBA)。
輸入a
,在第一分區設置可啟動標記。
輸入n
,enter
主分區,enter
第二個,enter
起始扇區,enter
結束扇區。
輸入w
,將改動寫入分區表。
格式化:
# mkfs.vfat -F 32 /dev/sdb1
# mkfs.ext4 /dev/sdb2
第一個分區用來放U-Boot,板子啟動后就會找一個叫MLO的文件,
編譯U-Boot后,就會生成一個MLO,不必擔心。
第二個分區用來放Linux系統。
sdb2被格式化后,里面自動有個lost+found,文件系統的結構中正好有一條:
/lost+found: Filesystem-specific recoverable data
我的文件系統目錄結構
/
/bin --> /usr/bin/
/boot/
/dev/
/etc/
/home/
/home/root
/lib --> /usr/lib/
/mnt/
/proc/
/run/
/sbin --> /usr/bin/
/sys/
/tmp
/usr/
/usr/app/
/usr/bin/
/usr/include/
/usr/lib/
/usr/sbin --> bin/
/usr/share/
/usr/share/man/
/var/
/var/run --> /run/
便簽
編譯時的一些配置等,我為了方便調用,放到這里。
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ export PATH=$PATH:/home/spy/Work/U-Boot/u-boot/tools
$ export CROSS_COMPILE=arm-none-linux-gnueabi-
# chown -R 0:0
# chgrp -v tty /usr/app/util-linux/bin/wall
# chown -R spy:users
找readelf結果中帶"Shared library"的行
readelf -ld | grep "Shared library"
以可讀寫重新掛載根文件系統
mount -n -o remount,rw /
放到交叉編譯器搜索庫中的程序庫
libcap,pam,ncurses,gdbm,db,iptables
coreutils與util-linux重復的命令。
kill
shadow與util-linux重復的命令。
{login,nologin,su}
shadow與coreutils重復的命令。
groups
下面是編譯軟件的過程
U-Boot
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ export CROSS_COMPILE=arm-none-linux-gnueabi-
$ make O=../u-boot distclean
$ make O=../u-boot am335x_boneblack_config
$ make O=../u-boot all
源碼中正好有am335x_boneblack這個配置文件,對應我的開發板,想知道可用的配置文件
看README吧,它會告訴你到另一個文件中找。
忘了是編譯U-Boot還是Linux內核的時候,提示我的系統中缺少bc,用你的包管理器安上就好了。
摘自TI的wiki,讓你了解MLO和u-boot.img是什么。
大概是AM335X芯片中的程序較小,只為了4種啟動方式初始化,
然后找到MLO,運行MLO進一步初始化,如DDR3內存。
MLO最后找到u-boot.img,運行之。u-boot.img這個名字應該是在MLO的代碼中指定的。
提示:TI的StarterWare中的2個文件叫MLO和app。
Two stage U-Boot design
This section gives an overview of the two stage U-Boot approach adopted for AM335X.
The size of the internal RAM in AM335X is 128KB out of which
18KB at the end is used by the ROM code. Also,
1 KB at the start (0x402f0000 - 0x402f0400) is secure and it cannot be accessed
This places a limit of 109KB on the size of the U-Boot binary which
the ROM code can transfer to the internal RAM and use as an initial stack
before initialization of DRAM.Since it is not possible to squeeze in all the functionality
that is normally expected from U-Boot in < 110KB (after setting aside some space
for stack, heap etc) a two stage approach has been adopted.
Initial stage initalize only the required boot devices (NAND, MMC, I2C etc);
2nd full stage initall all other devices (ethernet, timers, clocks etc).
The 1st binary is generated MLO and the 2nd stage is generated as u-boot.img.
編譯完后,現在就可以找找成就感了,
- 將MLO和u-boot.img復制到SD卡的第一個分區里;
- 將串口調試用的線與計算機相連,啟動串口調試程序,如Putty。
- 將SD卡插入開發板,保持按下板子上的啟動選擇鍵,插上電源,板子會從SD卡啟動,
- 可以松開啟動選擇鍵了。
Putty的窗口上會打印一些信息,你會看到一個1秒的倒計時,然后U-Boot會運行環境變量中
已經設置好的一些列命令,比如將Linux內核載入內存,但此時還沒有內核文件,U-Boot會
停留在它自己的命令行中,你可以輸入命令,如reset,這會讓板子重啟,倒計時的時候
按下計算機上的任意鍵,U-Boot就不會運行環境變量中的命令了,可玩一玩下面的演示。
給出U-Boot中的一些演示,輸入help可顯示所有命令。
help后接命令,可顯示該命令的幫助。如"help help"。
U-Boot# mmc rescan
U-Boot# mmc list
OMAP SD/MMC: 0
OMAP SD/MMC: 1
U-Boot# mmc dev
mmc0 is current device
U-Boot# mmc part
Partition Map for MMC device 0 -- Partition Type: DOS
Part Start Sector Num Sectors UUID Type
1 2048 131072 29942d7e-01 0c Boot
2 133120 15390720 29942d7e-02 83
U-Boot# ls mmc 0:1
100688 mlo
308232 u-boot.img
510 uenv.txt
3 file(s), 0 dir(s)
U-Boot# ls mmc 0:2
<DIR> 4096 .
<DIR> 4096 ..
<SYM> 7 bin
<DIR> 4096 boot
<DIR> 4096 dev
<DIR> 4096 etc
<DIR> 4096 home
<SYM> 7 lib
<DIR> 4096 lost+found
<DIR> 4096 mnt
<DIR> 4096 proc
<DIR> 4096 run
<SYM> 7 sbin
<DIR> 4096 sys
<DIR> 4096 tmp
<DIR> 4096 usr
<DIR> 4096 var
U-Boot# mmcinfo
Device: OMAP SD/MMC
Manufacturer ID: 3
OEM: 5344
Name: SU08G
Tran Speed: 50000000
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 7.4 GiB
Bus Width: 4-bit
U-Boot# mmc dev 1
mmc1(part 0) is current device
U-Boot# mmcinfo
Device: OMAP SD/MMC
Manufacturer ID: fe
OEM: 14e
Name: MMC02
Tran Speed: 52000000
Rd Block Len: 512
MMC version 4.41
High Capacity: No
Capacity: 1.8 GiB
Bus Width: 4-bit
U-Boot# mmc dev 0
mmc0 is current device
U-Boot#
fatls,ext4ls也可以顯示文件,但對應某種文件系統。
Linux
內核配置文件我在開發板的GitHub找的,
復制一份名字改.config
放到O指定的目錄中。
這樣就可以用make oldconfig
了。
我的內核版本是Linux-3.13.5,并沒有提示新的配置。
如果要修改的話,要注意systemd對內核的配置是有要求的,我并沒有改。
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ export PATH=$PATH:/home/spy/Work/U-Boot/u-boot/tools
$ export CROSS_COMPILE=arm-none-linux-gnueabi-
$ make O=../linux ARCH=arm help
$ make O=../linux ARCH=arm mrproper
$ make O=../linux ARCH=arm oldconfig
$ make O=../linux ARCH=arm LOADADDR=0x80008000 uImage
$ make O=../linux ARCH=arm LOADADDR=0x80008000 dtbs
$ make O=../linux ARCH=arm LOADADDR=0x80008000 modules
$ make O=../linux ARCH=arm LOADADDR=0x80008000 INSTALL_MOD_PATH=/home/spy/Work/root modules_install
$ make O=../linux ARCH=arm LOADADDR=0x80008000 INSTALL_FW_PATH=/home/spy/Work/fw firmware_install
$ make O=../linux ARCH=arm LOADADDR=0x80008000 INSTALL_HDR_PATH=/home/spy/Work/root/hdr headers_install
make help
是否指定ARCH,輸出內容是不同的。
uImage就是壓縮后的Linux內核,編譯時需要mkimage這個工具,編譯U-Boot的時候會生成那個工具,
所以我編譯內核之前指定了該工具的目錄。
- dts --> device tree source
- dtb --> device tree blob
- dtbo --> device tree blob overlay
- dtc --> device tree compile
沒研究為什么編譯設備樹是dtbs,是在make help的幫助中看到的。
內核的配置中指定了很多把驅動編譯成模塊的,這部分需要make modules
來單獨編譯。
我構建完的系統并沒有用到這些modules,插個U盤是可用的。
編譯完成后,可在輸出文件夾的arch/arm/boot
找到zImage, uImage和dts子文件夾,
dts里面有am335x-boneblack.dtb。zImage也是內核,也可以用,但我用的uImage。
最后那三個安裝目錄自己選個更好的吧。
modules會安在lib/modules
和lib/firmware
2個文件夾中。
安裝modules的時候,可能會提示depmod出問題,據說正常,那不是處理交叉編譯的模塊的。
但我還是看到了depmod后應該出現的modules.dep文件。
depmod是用來構建模塊依存關系的,目的是,當用insmod裝載某模塊的時候,可能會失敗,
因為這個模塊依賴的其他模塊還沒有裝入內核,depmod構建完依賴關系后,用另外一個命令
裝載模塊,即可自動裝載模塊所依賴的其他模塊。
firmware也安在lib/firmware
中,但與modules中的不同。我目前不知道firmware是干什么的。
headers還是需要安裝的,它就像glibc那樣,編譯程序時被調用。
下面可以驗證了。
將uImage文件,dts文件夾復制到SD卡第二個分區的/boot/里。
其實dts中應該只需am335x-boneblack.dtb。
啟動開發板前應該了解下U-Boot如何載入Linux。
U-Boot中最關鍵的環境變量
bootcmd: This variable defines a command string that is automatically executed
when the initial countdown is not interrupted.
This command is only executed when the variable bootdelay is also defined!
bootargs: The contents of this variable are passed to the Linux kernel as boot
arguments (aka "command line").
U-Boot中最關鍵的命令
run - run commands in an environment variable
U-Boot啟動后會運行bootcmd里的命令,所以要研究的話,從bootcmd開始。
提示:我的研究在后面。現在可不必研究直接看我的處理。
編譯后的U-Boot是不能引導uImage的,通過printenv看U-Boot的環境變量,可看到
它找的是zImage,而且am335x-boneblack.dtb是在/boot/中找,不是/boot/dts/。
如果將zImage和am335x-boneblack.dtb放到/boot中應該能引導。
為了符合我的要求,要修改環境變量,可以用editenv修改環境變量后saveenv。
但還是別這么做了,我不知道它把環境變量保存到哪里去了。我以為把MLO和u-boot.img換回
原始的可以恢復默認的環境變量值,但不是這樣,把SD卡格式化后也沒用。
有可能是存到eMMC中了,我想盡各種辦法破壞eMMC的數據,用了
dd if=/dev/zero of=/dev/mmcblk1
也不行。
除了mmcblk1還有個mmcblk1boot,可能是存到那里了,記著那個設備比較奇怪,沒有驗證。
最后用env default -a
然后saveenv
恢復默認了。
另一個方法是創建一個uEnv.txt文件,將它和MLO放到一起,內容如下。
該文件中的環境變量會覆蓋掉默認的環境變量。
bootfile=uImage
loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/dts/${fdtfile}
mmcloados=run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then bootm ${loadaddr} - ${fdtaddr}; else if test ${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; else bootz; fi;
mmcroot=/dev/mmcblk0p2 rw
mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} rootfstype=${mmcrootfstype} init=/usr/lib/systemd/systemd
一共6行,最后一行空白。
改動不是很大,bootfile修改了內核名字,
loadfdt中只是在目錄中加了“dts/”,
mmcloados主要是把bootz改成bootm。
mmcroot把只讀改成了讀寫,原因是systemd啟動后會創建一個machine-id到/etc中。
也許有其他辦法,如/etc/fstab文件,但我為了簡單沒創建那個文件。
mmcargs只是在后面指定了init為systemd,也有其他方法,如init為指向systemd的軟鏈接。
啟動BeagleBone Black,待內核啟動后會打印很多信息,最后你將目睹“kernel panic”,
這是因為我們目前并沒有init程序,為了找到成就感,你可以編譯下面
靜態鏈接的“Hello world!”,放到某個目錄中,然后在U-Boot的mmcargs變量中
把init指定為那個hello。
hello.c
#include <stdio.h>
int main(int argc, char *argv)
{
printf("Hello world!\n");
sleep(999999999);
}
$ arm-none-linux-gnueabi-gcc -o hello -static hello.c
再次啟動,最后你看到的將是"Hello world!"。
#我分析了U-Boot的引導過程,如下,再往下是我的演示。
gpio set 53; #這個是點亮LED燈的,其實并沒有這個,是我在其他的U-Boot中看到的。
i2c mw 0x24 1 0x3e; #這個也沒有,不知干什么的,激發下你的好奇心。
mmc dev 0; #將設備切換到`0`,即SD卡,其實一開始就是`0`,eMMC是`1`。
mmc rescan; #應該是檢測設備是否存在吧。
gpio set 54;
load mmc 0 0x80200000 uEnv.txt;
env import -t 0x80200000 $filesize #這2步是導入環境變量,filesize這個變量沒找到。
gpio set 55;
load mmc 0:2 0x80200000 /boot/uImage;
gpio set 56;
load mmc 0:2 0x80F80000 /boot/dts/am335x-boneblack.dtb;
setenv bootargs console=ttyO0,115200n8 ${optargs} root=/dev/mmcblk0p2 rw rootfstype=ext4 rootwait init=/usr/lib/systemd/systemd
bootm 0x80200000 - 0x80F80000;
#optargs這個環境變量也沒找到,在其他U-Boot中發現是quiet,
#作用是減少了內核打印的信息,systemd打印的信息也少了。
U-Boot# mmc dev 0
mmc0 is current device
U-Boot# mmc rescan
U-Boot# load mmc 0:2 0x80200000 /boot/uImage
3894896 bytes read in 237 ms (15.7 MiB/s)
U-Boot# load mmc 0:2 0x80F80000 /boot/dts/am335x-boneblack.dtb
17911 bytes read in 213 ms (82 KiB/s)
U-Boot# setenv bootargs console=ttyO0,115200n8 root=/dev/mmcblk0p2 rw rootfstype=ext4 rootwait init=/usr/lib/systemd/systemd
U-Boot# bootm 0x80200000 - 0x80F80000
## Booting kernel from Legacy Image at 80200000 ...
Image Name: Linux-3.13.5
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 3894832 Bytes = 3.7 MiB
Load Address: 80008000
Entry Point: 80008000
Verifying Checksum ... OK
## Flattened Device Tree blob at 80f80000
Booting using the fdt blob at 0x80f80000
Loading Kernel Image ... OK
Using Device Tree in place at 80f80000, end 80f875f6
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Initializing cgroup subsys cpuset
[ 0.000000] Initializing cgroup subsys cpu
[ 0.000000] Initializing cgroup subsys cpuacct
[ 0.000000] Linux version 3.13.5 (spy@alien) (gcc version 4.8.1 (Sourcery CodeBench Lite 2013.11-33) ) #1 SMP Sat Mar 8 20:26:55 UTC 2014
我并沒有導入uEnv.txt,它是下面這個樣子的。
U-Boot# load mmc 0 0x80200000 uEnv.txt
reading uEnv.txt
510 bytes read in 4 ms (124 KiB/s)
U-Boot# env import -t 0x80200000
## Info: input data size = 966 = 0x3C6
下面就是應用程序了,可根據我的提示自行安排順序,我曾經的順序不是這樣的。
主要的過程就是glibc,bash,coreutils,util-linux,systemd,shadow,
這些中間的都是被依賴的程序。
bash需要libgcc_s.so.1,它位于gcc中,但編譯gcc時間較長,gcc還依賴5個程序。
迫不及待驗證編譯結果的話可以先把交叉編譯器中的庫復制到開發板系統中,
我當初就那么做的。
glibc是程序庫了,幾乎動態鏈接的程序都要用到了,"Hello world"中的printf函數都需要。
bash是個shell,提供了人與計算機交互的界面。
coreutils里有很多常用命令,如ls
。
util-linux也是有很多命令,如mount,login,fdisk。
systemd是個init程序。
shadow是和登陸相關的,主要是把/etc/passwd
文件里的密碼變成“x”,
里面有login,passwd,useradd等命令。
其實系統系統后的第一個程序是systemd,但它的文檔較少,不了解是怎么工作的,
所以先保證其他的能工作,再研究。
至于怎么編譯,源碼中會有README,INSTALL等文檔。
需要注意的是我構建的系統與大多數Linux不同,我把所有程序都安裝在了
/usr/app/{程序}/中。這是我改進Linux的開始,咱有大計劃呀!
我的想法如下,
對于程序庫,依舊在/usr/lib/中搜索,里面除了子文件夾都是軟鏈接,指向app中
相應的程序庫。
對于/usr/bin/中的程序,也是軟鏈接。
對于/etc/中的配置文件,其實有些程序不在那個目錄找配置文件了,如果還在那個目錄找,
那么里面不是軟鏈接,就是傳統的配置文件。
glibc
/usr/local
->
<- bash, //這2個依賴和被依賴的,僅供參考,肯定不全的,下面也一樣。
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/glibc/glibc-build
$ ../glibc-2.19/configure --prefix=/usr/app/glibc --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi
$ make
$ make install install_root=/home/spy/Work/sources/glibc/glibc
源碼中并沒有makefile文件,這需要運行configure來生成。
通過“configure --help”查看可用的選項。
prefix為安裝目錄;
build和host用來指定系統類型。可看看autoconf的手冊中“Specifying target triplets”。
build - 構建程序的系統,在該系統中配置和編譯程序。
host - 構建后的程序運行在此系統中。
target - 編譯工具為此系統生成程序。
這個target,配置glibc不用指定,有的文檔中是這樣說的。
It
s for use when building compiler tools, with
--hostbeing where they will run, and
--targetwhat they
ll produce code for.
也就是說,通常用在構建編譯工具的時候,舉個例子,
如果你在你的x86_64計算機中編譯gcc,運行在你的x86_64系統中,讓gcc生成的程序也運行在x86_64中,
這三個是這樣的(為了突出重點,我簡化了等號右邊):
build=x86_64,host=x86_64,target=x86_64。
在Arch Linux中運行pacman -S gcc
安裝的就是這種(pacman是包管理器),
前2個條件不變,如果讓gcc生成的程序運行在arm中,就像我們要用的Sourcery CodeBench Lite,那么這三個是這樣的:
build=x86_64,host=x86_64,target=arm
。
第一個條件不變,如果讓gcc運行在arm中,生成的程序也運行在arm中,就像后面我們要編譯的,這三個是這樣的:
build=x86_64,host=arm,target=arm
。
等號右邊的格式如下:
cpu-company-system
system可以有以下2種格式之一
os
kernel-os
查看源碼中的config.sub文件,能看到各部分可能的值。
運行configure的時候,如果沒指定的話,configure會用config.guess這個腳本
猜出build的值。然后host默認等于build,target默認等于host。
config.guess通常和config.sub在一起,都可單獨運行的,有“--help”這個選項可用。
在我的系統中猜測出的值為“x86_64-unknown-linux-gnu”。
在BeagleBone Black中,結果為“armv7l-unknow-linux-gnueabihf”。這個結果應該是
不知道的,因為我是在BeagleBone Black已經可用的系統中運行的config.guess。
而我們的系統還沒有構建出來呢,不可能運行config.guess。
那么我們如何確定host呢,難道要看config.sub?不必。
當build和host不同的時候,configure會啟用交叉編譯模式。
它會檢測以host值為前綴的編譯工具,如arm-none-linux-gnueabi-gcc,然后使用這個
帶前綴的gcc。所以我們的host指定為我們交叉編譯器中命令的前綴就好了。
我嘗試過了,如果host是“abcdefg”這種,大概會提示無效的值;
如果是“arm-none-linux-gnueabihf”這種有效的,configure會檢測
arm-none-linux-gnueabihf-gcc,他是找不到的。
既然build可以猜測出來,那是否還要指定呢,我開始就是沒指定,可以的。但是autoconf手冊中:
“For historical reasons, whenever you specify --host, be sure
to specify --build too; this will be fixed in the future.”
看來這個“future”已經到了,但我還是指定了。
我們編譯程序是用在其他的系統中,如果不指定install_root,它就要往prefix指定的目錄安了,
不要這樣,我的路徑還好,如果是/usr/lib/,那就破壞了你正使用的系統,不過我用的是普通用戶,
應該沒權限往那個目錄安。
有了install_root,就會安裝到/home/spy/Work/sources/glibc/glibc/usr/app/glibc,看看
makefile文件就會明白原理。
那么把prefix指定為那個完整的目錄,不使用install_root行不行呢。最好別這樣。
構建完glibc,其中的ld.so并不是在/lib/或/usr/lib/中找程序庫,而是在glibc的庫
被安裝到的目錄中找,即/usr/app/glibc/lib
。
可見,這個prefix會記錄到生成的程序中,所以最好別讓目錄那么長,那么亂。
問題又來了,ld.so竟然不在/usr/lib/中找程序庫,這可是傳說中的默認目錄啊。
我的處理是在構建的系統中運行ldconfig,在bash中說。
gmp
/usr/local
->
<- mpfr, mpc, isl, cloog, gcc
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/gmp/gmp-build
$ ../gmp-5.1.3/configure --prefix=/usr/app/gmp --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi
$ make
$ make install DESTDIR=/home/spy/Work/sources/gmp/gmp
這個和下面的4個都是為gcc服務的。
這里的安裝目錄用的是DESTDIR,看看源代碼里的文檔吧。
libtool: install: warning: remember to run 'libtool --finish /usr/local/lib'
安裝時遇到個警告,這個應該和ldconfig這個命令有關,應該是為了glibc的ld程序能找到這個庫,我沒執行這一步。
mpfr
/usr/local]
-> gmp,
<- mpc, gcc
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/mpfr/mpfr-build
$ ../mpfr-3.1.2/configure --prefix=/usr/app/mpfr --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --with-gmp=/home/spy/Work/sources/gmp/gmp/usr/app/gmp
$ make
$ make install DESTDIR=/home/spy/Work/sources/mpfr/mpfr
修改lib/libmpfr.la,
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /usr/app/gmp/lib/libgmp.la'
到
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib/libgmp.la'
也就是把目錄改成完整的,這個過程的原因見mpc。
libtool: install: warning: remember to run 'libtool --finish /usr/local/lib'
警告依舊,不管他,怕他影響我正使用系統中的庫。
應該還會看到其他警告,如某個.la文件被moved,答案也是見mpc。
mpc
/usr/local
-> gmp,mpfr
<- gcc
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/mpc/mpc-build
$ ../mpc-1.0.2/configure --prefix=/usr/app/mpc --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --with-gmp=/home/spy/Work/sources/gmp/gmp/usr/app/gmp --with-mpfr=/home/spy/Work/sources/mpfr/mpfr/usr/app/mpfr
$ make
$ make install DESTDIR=/home/spy/Work/sources/mpc/mpc
修改.la文件,
# Libraries that this one depends upon.
dependency_libs=` -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib -L/home/spy/Work/sources/mpfr/mpfr/usr/app/mpfr/lib /usr/app/mpfr/lib/libmpfr.la /usr/app/gmp/lib/libgmp.la -lm`
到
# Libraries that this one depends upon.
dependency_libs=` -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib -L/home/spy/Work/sources/mpfr/mpfr/usr/app/mpfr/lib /home/spy/Work/sources/mpfr/mpfr/usr/app/mpfr/lib/libmpfr.la /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib/libgmp.la -lm`
libtool: install: warning: remember to run 'libtool --finish /usr/local/lib'
找不到*.la的問題
/usr/bin/sed: can't read /usr/app/gmp/lib/libgmp.la: No such file or directory
libtool: link: '/usr/app/gmp/lib/libgmp.la' is not a valid libtool archive
如果沒有我mpfr中的修改,編譯時就會出現這個錯誤。
編譯mpc的時候,應該讀取了mpfr中的libmpfr.la。然后根據dependency_libs中的路徑
找libgmp.la,結果找不到。
dependency_libs的前一部分,應該是由我配置時指定的--with-gmp來確定,
而后半部分,看看安裝后的gmp,也有個.la文件,文件中有個libdir,
# Directory that this library needs to be installed in:
libdir='/usr/app/gmp/lib'
很明顯,這個libdir是根據編譯gmp時指定的prefix確定的。
如果把libdir手動改成/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib
,
再編譯mpfr,libmpfr.la中的dependency_libs就是,
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib/libgmp.la'
這樣也不會出現lib*.la文件被moved的警告。
由此可見,后半部分是由所依賴的.la文件中libdir確定的。
我最后沒有選擇修改libdir,雖然修改libdir也可以編譯成功,而且沒有警告。
isl
/usr/local
-> gmp
<- gcc
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/isl/isl-build
$ ../isl-0.12.2/configure --prefix=/usr/app/isl --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --with-gmp-prefix=/home/spy/Work/sources/gmp/gmp/usr/app/gmp --with-gmp-exec-prefix=/home/spy/Work/sources/gmp/gmp/usr/app/gmp
$ make
$ make install DESTDIR=/home/spy/Work/sources/isl/isl
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /usr/app/gmp/lib/libgmp.la'
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib/libgmp.la'
libtool: warning: remember to run 'libtool --finish /usr/local/lib'
cloog
/usr/local
-> isl,gmp
<- gcc
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/cloog/cloog-build
$ ../cloog-0.18.1/configure --prefix=/usr/app/cloog --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --with-isl=system --with-isl-prefix=/home/spy/Work/sources/isl/isl/usr/app/isl --with-isl-exec-prefix=/home/spy/Work/sources/isl/isl/usr/app/isl --with-gmp-prefix=/home/spy/Work/sources/gmp/gmp/usr/app/gmp --with-gmp-exec-prefix=/home/spy/Work/sources/gmp/gmp/usr/app/gmp
修改Makefile libcloog-isl.la:
libcloog_isl_la_LDFLAGS = -version-info 4:0:0 \
-L/home/spy/Work/sources/isl/isl/usr/local/lib
am_libcloog_isl_la_rpath = -rpath /home/spy/Work/sources/isl/isl/usr/app/isl/lib
$ make
$ make install DESTDIR=/home/spy/Work/sources/cloog/cloog
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/isl/isl/usr/app/isl/lib -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /usr/app/isl/lib/libisl.la /usr/app/gmp/lib/libgmp.la'
# Libraries that this one depends upon.
dependency_libs=' -L/home/spy/Work/sources/isl/isl/usr/app/isl/lib -L/home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib /home/spy/Work/sources/isl/isl/usr/app/isl/lib/libisl.la /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib/libgmp.la'
--with-isl=system 目的是使用之前編譯的isl,cloog源碼中有也有份isl。
編譯時錯誤undefined reference
warning: libisl.so.10, needed by ./.libs/libcloog-isl.so, not found (try using -rpath or -rpath-link)
rpath的問題,解決見make前操作。
一看和isl有關,cloog的源碼中自帶了一份isl,我配置時沒用cloog中的,
當出現這個錯誤時,我配置成用cloog中的,這樣編譯cloog的時候,會有編譯isl的過程,
結果編譯isl的時候就有錯誤。
看了一下cloog中的isl,版本低一些。
我單獨編譯這個isl或者在isl官網下載同樣的版本,都是出錯,為了解決cloog的錯誤,
我決定先解決簡單一些的isl的錯誤入手。
isl-0.12.1版本的問題:
isl_polyhedron_sample
isl_polytope_scan
isl_polyhedron_detect_equalities
isl_cat
isl_closure
以上就是編譯時出現過的有問題的make對象。在makefile文件中,用了下面的rpath即可解決。
-rpath /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib
rpath是運行時搜索程序庫的路徑,是gcc的參數,可以寫到編譯后的程序中的。
也許還有別的對象,但都用LINK
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
更改 AM_LDFLAGS = -rpath /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib
就都解決了。
但我發現了另一個方式,把這一個改對就可以
libisl.la: $(libisl_la_OBJECTS) $(libisl_la_DEPENDENCIES) $(EXTRA_libisl_la_DEPENDENCIES)
$(AM_V_CCLD)$(libisl_la_LINK) -rpath /home/spy/Work/sources/gmp/gmp/usr/app/gmp/lib $(libisl_la_OBJECTS) $(libisl_la_LIBADD) $(LIBS)
我不知道這樣做是否會出現什么問題,但還沒遇到,除了下面沒造成什么影響的。
按這個方式修改cloog的makefile的時候,
.la文件中,安裝路徑里出現的應該是cloog啊,竟然是isl,我做了修改,
# Directory that this library needs to be installed in:
libdir='/home/spy/Work/sources/isl/isl/usr/app/isl/lib'
改成
# Directory that this library needs to be installed in:
libdir='/usr/app/cloog/lib'
gcc
/usr/local
-> gmp,mpfr,mpc,isl,cloog
<- bash
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/gcc/gcc-build
$ ../gcc-4.8.2/configure --prefix=/usr/app/gcc --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --target=arm-none-linux-gnueabi --enable-shared --enable-threads --enable-languages=c --with-gmp=/home/spy/Work/sources/gmp/gmp/usr/app/gmp --with-mpfr=/home/spy/Work/sources/mpfr/mpfr/usr/app/mpfr --with-mpc=/home/spy/Work/sources/mpc/mpc/usr/app/mpc --with-isl=/home/spy/Work/sources/isl/isl/usr/app/isl --with-cloog=/home/spy/Work/sources/cloog/cloog/usr/app/cloog
$ make
$ make install DESTDIR=/home/spy/Work/sources/gcc/gcc
gcc就是編譯器了,我們編譯軟件就靠它了,gcc也包含一些庫,我們的bash要用到。
gcc的配置選項太多了,我只額外用了
--enable-shared --enable-threads --enable-languages=c
也不知道是否需要。
我編譯了50分鐘,現在是2014年,我這2手筆記本比較差了,
CPU 1.86GHz,內存2G的,DDR2的,單條1G。
bash
-> glibc,gcc
<-
$ export PATH=$PATH:/home/spy/Software/arm-2013.11/bin
$ cd /home/spy/Work/sources/Bash/bash-build
$ ../bash-4.3/configure --prefix=/usr/app/bash --build=x86_64-unknown-linux-gnu --host=arm-none-linux-gnueabi --without-bash-malloc --with-installed-readline
$ make
$ make DESTDIR=/home/spy/Work/sources/Bash/bash install
--without-bash-malloc --with-installed-readline
對這2個不太了解,只是為了bash簡單點。
不知道readline干什么的,我構建的系統沒有安readline。
在這里推薦個工具,readelf,這是binutils中的一個命令。可查看二進制文件的信息,
我用它主要是為了看到所依賴的庫。
$ readelf -hld bin/bash
-a選項是打印所有的信息,我們用-hld就可以了。
看看我們編譯后的bash,它需要gcc中的libgcc_s.so.1,而我電腦中的bash卻不需要那個庫。
有了shell,我們就可以在開發板中驗證我們編譯的結果了。
至此,我們編譯了glibc,gmp,mpfr,mpc,isl,cloog,gcc,bash。
在復制到SD卡之前,我想應該先把mpfr,mpc,isl,cloog中的改動改回來。
根據prefix,將編譯后的程序復制到SD卡,/usr/lib/和/usr/bin/中添加相應的軟鏈接。
我并不是在這個時候才制作的軟鏈接,每編譯過一個程序,發現有bin和lib文件夾,
就把軟鏈接做好了,直接復制到SD卡中就可以了。
至于怎么制作,我不擅長,寫個腳本應該比較好,但我沒學。
/etc/中不必有配置文件。
在uEnv.txt中,把bootargs的init改為/usr/bin/bash
。
如果就這樣啟動的話,會提示找不到libgcc_s.so.1
,因為這個庫不在ld.so的搜索路徑中。
目前的搜索路徑是/usr/app/glibc/lib
。
所以/usr/app/glibc/lib
中有個libgcc_s.so.1
就可以了,可以放一個軟鏈接,就像/usr/lib
中的。
不要擔心,這只是臨時的。
接下來我們要在開發板中運行ldconfig,這是glibc的一部分,它會讀取ld.so.conf文件中的
路徑,然后建立一個ld.so.cache文件,這樣ld.so就能利用ld.so.cache找到那些路徑中的庫了。
ld.so.conf文件不存在,而我們構建的系統還沒有文本編輯器,所以要在啟動之前建立該文件。
內容就是
/usr/lib
很多文本文件都以空行結尾,我們也這么做吧。
那么這個文件要放到哪里呢,一般是/etc/中,但在我們構建的系統中,
ldconfig是到/usr/app/glibc/etc/
中找,所以要放到這個目錄中。
好,可以啟動開發板了,進到shell之后,運行ldconfig。
把/usr/app/glibc/lib中的libgcc_s.so.1刪掉,重啟,看看是不是可以正常運行了?
這樣的方法并不好,應該讓編譯glibc的時候可以指定ld.so的搜索路徑。
其實我當初用了更不好的方法,我在內核參數中指定了LD_LIBRARY_PATH。
雖然我們沒安什么程序,但bash是有內置命令的,比如切換目錄的cd
,查看
當前目錄的pwd
。
因為太長,分了2部分。
構建Linux筆記v1.0(第二部分)