Linuxコマンドをコンパイルしてみる（クロスコンパイルの練習）

# Linuxコマンドをコンパイルしてみる（クロスコンパイルの練習）
クロスコンパイルの練習例として、MIPS CPUのLinux上で動作するプログラムをUbuntu上で作成してみます。
ハンズオン等だと、いつもなら使えるLinuxコマンドが組み込み機器では無いことが多く、困ってしまう場合もあります。
今回は、そのような場合を想定した例になります。

**ご注意 ：**
1)この手順での動作保証は一切しません、ご了承ください。
実行する組み込み用ハードウェア、それぞれに適した環境が必要になるため。

2)こちらの記事で紹介した環境を利用しますので、記事を見て環境を用意ください。
[Mac上で動作するVirtualBoxのUbuntu上でGccクロスコンパイル環境をDockerで構築](https://qiita.com/tagucchan/items/e54b08acde0e5a317a56)

## Dcckerの起動

```
$ sudo docker start gcc-ubuntu
$ sudo docker exec -it gcc-ubuntu /bin/bash
```

## Linuxコマンドのソースコード
今回はsleepenhコマンドを例とします。
探しやすかったので、Debianから持ってきます。これで良いのかはわかりません！

[Debian · GitLab](https://salsa.debian.org/debian)
[sleepenh](https://salsa.debian.org/debian/sleepenh)


参考：コマンドが含まるパッケージを探す方法（Ubuntuでの例）
コマンドが【有る】マシンで実行してください。

```
root@af48c1a975a2:~# which bash # コマンドの場所を確認
/bin/bash
root@af48c1a975a2:~# dpkg --search /bin/bash # dpkgで調べられる
bash: /bin/bash
```

「bash」パッケージに/bin/bashコマンドが含まれることがわかります。

## ソースコードを入手する
パッケージをソースコードで入手

```
root@af48c1a975a2:~/dev# mkdir sleepenh # 作業ディレクトリを作成
＜docker環境でのファイルの取得や転送方法は、各自環境に合わせて実施ください　省略＞
sleepenh-master.zip　を手に入れた
root@af48c1a975a2:~/dev/sleepenh# unzip sleepenh-master.zip
root@af48c1a975a2:~/dev/sleepenh# cd sleepenh-master
```

## コンパイルする

Makefaileのままコンパイルするとクロスコンパイルにならないので、Makefileを修正します。

```
root@af48c1a975a2:~/dev/sleepenh/sleepenh-master# vi Makefile
```

修正箇所は　「CC =」の場所、gcc　を mips-linux-gnu-gcc　に変更します。
これで、PCでMIPS用のsleepenhをコンパイルできます。（クロスコンパイル）

```makefile
Q ?= @
ifneq ($(Q),@)
Q :=
endif

CC = mips-linux-gnu-gcc
CFLAGS = -std=gnu11 -Wall -Wextra -O2 $(EXTRA_CFLAGS)
＜＜　以下省略　＞＞
```

makeします

```
root@af48c1a975a2:~/dev/sleepenh/sleepenh-master# make
/bin/sh: 1: git: not found
mips-linux-gnu-gcc -std=gnu11 -Wall -Wextra -O2  -DVCSVERSION=''    sleepenh.c   -o sleepenh
gzip -9 < sleepenh.1 > sleepenh.1.gz

root@af48c1a975a2:~/dev/sleepenh/sleepenh-master# ls
Makefile  Makefile.org  changelog  debian  sleepenh  sleepenh.1  sleepenh.1.gz  sleepenh.c
```

sleepenh がコンパイルできました。

## ダイナミックリンク　と　スタティックリンク
プログラムを簡単に書くためには、用意されているライブラリを活用することが大事です。
コマンド等でも多く使われています。
このとき、PCやサーバであれば、ライブラリの不具合があればライブラリだけ更新されるのが便利です。
そのため、プログラムからは、動的にライブラリを参照する（ダイナミック リンク）ようにコンパイルするのが普通です。

ただ、組み込み環境などでは、ライブラリが無い、バージョンが揃っていないなど様々な環境でも動作すると便利です。
そのため、参照するライブラリを固定してしまうために、実行バイナリにライブラリを組み込んだ形（スタティック リンク）でコンパイルし、外部環境に左右されないようにすることがあります。

gcc に -static オプションを付加してコンパイルすると「スタティック リンク」、付加しないと「ダイナミック リンク」になります。

## 作成できたファイルの情報を確認

実際に作成できたsleepenhコマンドを確認してみます。

```
root@af48c1a975a2:~/dev/sleepenh/sleepenh-master# file sleepenh
sleepenh: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), dynamically linked, interpreter /lib/ld., for GNU/Linux 3.2.0, with debug_info, not stripped
```

MIPS用（ MIPS, MIPS-I version 1 (SYSV) ）で、ダイナミック リンク（ dynamically linked ）の実行ファイルができたことがわかります。
ダイナミック リンクですから、動作環境に依存してしまいます。
場合によっては参照するライブラリが無い可能性もあります。

## スタティック リンクでコンパイルする
そこで、スタティック リンクでコンパイルしてみます。

後で比較できるように、ディレクトリごとコピーして、準備します。

```
root@af48c1a975a2:~/dev/sleepenh# cd ~/dev/sleepenh/
root@af48c1a975a2:~/dev/sleepenh# cp -r sleepenh-static　sleepenh-static
root@af48c1a975a2:~/dev/sleepenh# cd sleepenh-static
root@af48c1a975a2:~/dev/sleepenh/sleepenh-static# make clean #掃除
```

スタティック リンクでコンパイルするため、Makefileを修正します。

```
root@af48c1a975a2:~/dev/sleepenh/sleepenh-static# vi Makefile
```

修正箇所は　「CFLAGS =」の場所に「 -static 」を追加します。
これで、PCでMIPS用のsleepenhをスタティック リンクでコンパイルできます。（クロスコンパイル）

```makefile:変更前
CFLAGS = -std=gnu11 -Wall -Wextra -O2 $(EXTRA_CFLAGS)
```

```makefile:変更後
CFLAGS = -std=gnu11 -Wall -static -Wextra -O2 $(EXTRA_CFLAGS)
```

```makefile:変更後
Q ?= @
ifneq ($(Q),@)
Q :=
endif

CC = mips-linux-gnu-gcc
CFLAGS = -std=gnu11 -Wall -static -Wextra -O2 $(EXTRA_CFLAGS)
＜＜　以下省略　＞＞
```

makeします

```
root@af48c1a975a2:~/dev/sleepenh/sleepenh-static# make
/bin/sh: 1: git: not found
mips-linux-gnu-gcc -std=gnu11 -Wall -static -Wextra -O2  -DVCSVERSION=''    sleepenh.c   -o sleepenh
gzip -9 < sleepenh.1 > sleepenh.1.gz

root@af48c1a975a2:~/dev/sleepenh/sleepenh-master# ls
Makefile  Makefile.org  changelog  debian  sleepenh  sleepenh.1  sleepenh.1.gz  sleepenh.c
```

sleepenh がコンパイルできました。

## 作成できたファイルの情報を確認（スタティック リンクになったかどうか）

実際に作成できたsleepenhコマンドを確認してみます。

```
sleepenh: ELF 32-bit MSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, with debug_info, not stripped
```

MIPS用（ MIPS, MIPS-I version 1 (SYSV) ）で、スタティック リンク（ statically linked ）の実行ファイルができたことがわかります。
スタティック リンクであれば、動作環境に依存せず動作します。
場合によっては参照するライブラリが無い可能性もありますが、問題ありません。

このスタティック リンクでコンパイルした方を、MIPS Linuxの装置に持っていき、動作試験をしてみてください。