ラズパイ用のsquidをクロスビルドしたときの話

# 本記事の想定する読者

1. RaspberryPiにUbuntuをインストールしている。
1. 同じバージョンのOSが入った開発マシン(のsudo権限)を持っている。
1. 非力なRaspberryPiではなく、高速なマシンでdpkg-buildpackageしたい。
1. とは言っても仮想環境のオーバーヘッドは削減したい。

~~つまり自分~~
# まえがき
何時だったか、オリンピックを目前に控え、世間で『GWは我慢のWeek』なんて迷言が言われていた頃、ラズパイを手に入れた私は、自宅に引きこもってUbuntuを入れたりしていろいろ遊んでいたのでした。 
そして、何やかんやあって、SquidをARM向けにコンパイルしたのでした。
態々ソースコードからビルドしたい理由について説明するには人生が短すぎるため、[先人の知恵](https://qiita.com/ShortArrow/items/474d62ee984f93b4232f)を有り難く拝借し、本記事では、クロスビルドに話を限定したい。
記憶も朧になっているので、間違えていたらごめんなさい。

# QEMU
QEMUはLinuxで動くCPUエミュレータです。これを使うと、ARM向けの実行ファイルを、あたかもx86用のバイナリであるかのように動かすことができます。

QEMUにはOSやベアメタルをエミュレートする『システムエミュレーション』(とても遅い)と実行ファイル単体をエミュレートする『ユーザーモードエミュレーション』があります。

今回はqemu-user-binfmtという『ユーザーモードエミュレーション』のパッケージをインストールして使用します。ついでに必要なソフトを全部入れてしまいましょう。

```
$ sudo apt install qemu-user-binfmt build-essential g++-aarch64-linux-gnu
```

# chroot環境準備
chrootは、ルートディレクトリを切り替えるコマンドです。Linuxでは、このコマンドで仮想環境らしきものを実現することができます[^chroot]。
[^chroot]: Dockerでやりたいことの半分くらいは実現できると思っている。

### ルートディレクトリを用意する

ルートディレクトリのイメージを入手し適切なディレクトリに展開します。
今回は下記のリンクを使用しましたが、実機から吸い上げても良かったかもしれません。
ファイル名の末尾がarm64-root.tar.xzとなっているのが64bitARM用です。

http://ubuntutym2.u-toyama.ac.jp/cloud-images/releases/20.04/release/

```
crossroot=/<適切なファイルパス>/arm64
$ sudo tar -xvf xxx-arm64-root.tar.xz -C $crossroot
```

### ホスト環境とファイルを共有する。
chrootによる仮想環境は、まともな仮想環境と比べて全てが丸見えです。これを有効活用します。
おおよその手順は以下の通り

1. ホスト環境の/を$crossroot/hostrootに`mount --bind`する。
1. $crossrootから/hostrootにシンボリックリンクを張る。
1. スペシャルファイル用のディレクトリをmountする。

最低限必要なファイルは、以下の通り

+ /lib64配下
+ /lib/x86_64-linux-gnu配下
+ /usr/lib/x86_64-linux-gnu配下
+ /usr/bin/qemu-aarch64

しかし、私の環境では、ホストとchrootでディレクトリ構成が微妙に異なっており、aufsで辻褄を合わせる必要がありました。

```
$ sudo ln -s /hostroot/lib64 $crossroot/lib64
$ sudo ln -s /hostroot/usr/bin/qemu-aarch64 $crossroot/usr/bin/
```

mountは再起動するたびに戻ってしまうので、シェルスクリプトにまとめておくのが良いでしょう。

```bash:chroot_env.sh
crossroot=<適切なファイルパス>
for pos in sys proc
do
	mount --bind /$pos $crossroot/$pos
done
mount -t devtmpfs udev $crossroot/dev
mount -t aufs -o br:/lib/x86_64-linux-gnu=ro,br:/usr/lib/x86_64-linux-gnu=ro none $crossroot/usr/lib/x86_64-linux-gnu
mount --bind -o ro / $crossroot/hostroot
```
#chroot環境に入る(root)
chrootする前にやっておくべき事があります。
以下の操作によって、chrootした際にプロンプトが(arm)$のように変化し、深刻なミスを防ぐことができます(bashのみ)。

```
$ echo arm64 |sudo dd of=$crossroot/etc/debian_chroot
```
いよいよchrootですが、シェルがbashでない場合は安全のためにbashと打ってください。exitまたはCtrl+Dで脱出します。
$は一般ユーザのプロンプト、#はrootユーザのプロンプトを表しています。

```
$ sudo chroot $hostroot
(arm64)# exit
$
```
# ソフトウェアのアップデート
ソフトウェアのアップデートと行きたいところですが、chroot環境では何故かDNSが息をしていないため、サーバにアクセスできません。
強引ですが、今回はプロキシを**IPアドレスで**指定することで回避しました。
環境が不完全なため警告が出ますが、無視します。

```
(arm64)# export http_proxy=http://192.168.0.12:3128/
(arm64)# apt update
(arm64)# apt upgrade
```

# gccを加速する
ビルドにおいて、最もCPUリソースを食うのはgccだと思うので、QEMU上ではなく、ネイティブに実行するようにします。

```
(arm64)# cd /usr/bin
(arm64)# rm aarch64-linux-gnu-gcc-9 aarch64-linux-gnu-g++-9 #<-- インストールされたバージョンによって異なる
(arm64)# ln -s /hostroot/usr/bin/aarch64-linux-gnu-gcc-9 #<-- インストールされたバージョンによって異なる
(arm64)# ln -s /hostroot/usr/bin/aarch64-linux-gnu-g++-9 #<-- インストールされたバージョンによって異なる
(arm64)# ln -s /hostroot/usr/aarch64-linux-gnu /usr/
(arm64)# ln -s /hostroot/usr/lib/gcc-cross /usr/lib/
```

# fakerootを修正する
fakerootはファイルの所有者を偽装するコマンドで、tarを固める際に利用されている様です。
ARM版のfakerootがQEMU上で動かない[^fakeroot]ので、~~update-alternativesコマンドでfakeroot-tcpに切り替えます。~~
ネイティブ版を使用したより良さそうな方法を考えました。

[^fakeroot]: `qemu: Unsupported syscall` みたいな感じのエラーが出る。

```
(arm64)# cd /usr/bin
(arm64)# rm fakeroot-sysv faked-sysv
(arm64)# ln -s /hostroot/usr/bin/fakeroot-sysv
(arm64)# ln -s /hostroot/usr/bin/faked-sysv
(arm64)# cd
(arm64)# touch dummy.c
(arm64)# gcc -shared -fPIC -o libfakeroot-sysv.so dummy.c
(arm64)# mv libfakeroot-sysv.so /lib/aarch64-linux-gnu
(arm64)# rm dummy.c 
```

# chroot環境に入る(一般ユーザ)
ログイン情報等が記述されている3ファイルをホスト環境から持ってきます。また、ホームディレクトリを用意します。

```
(arm64)# cd /etc
(arm64)# rm passwd shadow group
(arm64)# ln -s /hostroot/etc/passwd
(arm64)# ln -s /hostroot/etc/shadow
(arm64)# ln -s /hostroot/etc/group
(arm64)# mkdir /home/<自分のユーザ名>
(arm64)# chown <自分のユーザ名> /home/<自分のユーザ名>
(arm64)# exit
$ sudo -E chroot --userspec=$USER $hostroot
(arm64)$
```

漸く一般ユーザでログインできました。後は通常の環境とほぼ同様なため、割愛したい。~~執筆疲れた~~
テキストファイルの編集等、ARMと関係ない作業は、chroot環境ではなく、ホスト環境でやった方が良いと思います。
最後に、lintianの実行中にフリーズしますが、ビルドは終わっているので、Ctrl+Cで止めて問題ないです。

```
(arm64)# apt build-dep squid
(arm64)# apt install libssl-dev
(arm64)# exit
$ cd $crossroot/home/<自分のユーザ名>/
$ mkdir dpkg-build
$ cd dpkg-build
$ apt source squid
$ gedit squid-4.x/debian/rules #<-- ファイルの編集
$ sudo -E chroot --userspec=$USER $hostroot
(arm64)$ cd ~/dpkg-build/squid-4.x #<-- インストールされたバージョンによって異なる
(arm64)$ debuild -uc -us
```

# 宿題
### ビルドの高速化
さらなる高速化の為に、ツールをネイティブ版とすげ替えたい。以下が置き換え候補。

+ perl
+ sed
+ sh(`dpkg-reconfigure`によるdash/bashの切り替え含む)

### エラーの解消
特にlintianで止まるやつ。

### より複雑な構成

1. ホスト環境: Debian/chroot環境: Ubuntu
1. ホスト環境: Ubuntu18/chroot環境: Ubuntu20

などを想定

# 参考

https://msyksphinz.hatenablog.com/entry/2020/11/17/040000

https://qiita.com/autch/items/c8c9cdc7b8e5821e81a4