构建Linux笔记v1.0(第一部分)

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个部分,

  1. bootloader,即启动引导程序,我选择U-Boot。
  2. Linux内核。
  3. 应用程序。

我还没研究是什么决定了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,在第一分区设置可启动标记。
输入nenter主分区,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.


编译完后,现在就可以找找成就感了,

  1. 将MLO和u-boot.img复制到SD卡的第一个分区里;
  2. 将串口调试用的线与计算机相连,启动串口调试程序,如Putty。
  3. 将SD卡插入开发板,保持按下板子上的启动选择键,插上电源,板子会从SD卡启动,
  4. 可以松开启动选择键了。

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
       4096 .
       4096 ..
          7 bin
       4096 boot
       4096 dev
       4096 etc
       4096 home
          7 lib
       4096 lost+found
       4096 mnt
       4096 proc
       4096 run
          7 sbin
       4096 sys
       4096 tmp
       4096 usr
       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/moduleslib/firmware2个文件夹中。


安装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 

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不用指定,有的文档中是这样说的。

Its for use when building compiler tools, with--hostbeing where they will run, and--targetwhat theyll 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(第二部分)

你可能感兴趣的:(构建Linux笔记v1.0(第一部分))