前回 は、VirtualBox + Ubuntu 22.04 のネットワークの理解を進めました。
今回は、ここで作った QEMU を使って、VirtualBox + Ubuntu 22.04 とのネットワークの疎通を確認していきます。
それでは、やっていきます。
はじめに
「QEMUを動かす」の記事一覧です。良かったら参考にしてください。
QEMUを動かすの記事一覧
以下は、Buildroot の Documentation のリンクです。
https://buildroot.org/downloads/manual/manual.html
ここで使用する環境は、VirtualBox に入れた Ubuntu 22.04 です。
Buildroot を使います。Buildroot に含まれている U-Boot と、QEMU を使います。
ツール |
設定値 |
Buildroot |
qemu_aarch64_virt_defconfig |
U-Boot |
qemu_arm64_defconfig |
QEMU |
virt(virt-8.2) |
IPv6 の無効化
まずは、パケットキャプチャで見にくくなるので、VirtualBox + Ubuntu 22.04 の環境で、IPv6 を無効化します。
IPv6 を無効化する方法は、一時的に IPv6 を無効化し、再起動したら元に戻る方法と、再起動しても IPv6 が無効のままになる方法があります。
現在の状況を確認します。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:54:d5:cb brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
valid_lft 86331sec preferred_lft 86331sec
inet6 fe80::eb16:b65:e9c5:ddc9/64 scope link noprefixroute
valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:c0:67:7d brd ff:ff:ff:ff:ff:ff
inet 192.168.56.101/24 brd 192.168.56.255 scope global dynamic noprefixroute enp0s8
valid_lft 531sec preferred_lft 531sec
inet6 fe80::2d19:efaa:a12d:f974/64 scope link noprefixroute
valid_lft forever preferred_lft forever
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:6c:c6:1a:3c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 が表示されているので、現在は IPv6 が有効な状態です。
まず、一時的に IPv6 を無効化する方法です。これは再起動すると元に戻ります。
sysctl コマンドを使います。
sysctl コマンドは、カーネルのパラメータを変更するコマンドです。以下のように、-w オプションを使って、パラメータを設定すると、その設定が即座にシステムに反映されます。
$ sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
$ sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
$ sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1
一方、永続的に IPv6 を無効化する方法は、2つあります。
1つは、/etc/sysctl.conf に設定を書き込んで、起動時に実行されるスクリプトを作成して、/etc/sysctl.conf を反映させるという方法がありますが、なんか自然な方法ではない気がするので、ここではもう1つの方法で無効化します。
ブートローダーの grub の設定ファイル(/etc/default/grub)で、カーネルパラメータとして、追加で、ipv6.disable=1
を渡す方法です。
/etc/default/grub を編集して、カーネルパラメータに ipv6.disable=1
を追加します。
-GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
+GRUB_CMDLINE_LINUX_DEFAULT="quiet splash ipv6.disable=1"
追加できたら、grub を更新します。
$ sudo update-grub
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.5.0-41-generic
Found initrd image: /boot/initrd.img-6.5.0-41-generic
Found linux image: /boot/vmlinuz-6.5.0-35-generic
Found initrd image: /boot/initrd.img-6.5.0-35-generic
Found memtest86+ image: /boot/memtest86+.elf
Found memtest86+ image: /boot/memtest86+.bin
Warning: os-prober will not be executed to detect other bootable partitions.
Systems on them will not be added to the GRUB boot configuration.
Check GRUB_DISABLE_OS_PROBER documentation entry.
done
更新できたら、再起動します。
再度、現在の状態を確認します。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:54:d5:cb brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
valid_lft 86275sec preferred_lft 86275sec
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:c0:67:7d brd ff:ff:ff:ff:ff:ff
inet 192.168.56.101/24 brd 192.168.56.255 scope global dynamic noprefixroute enp0s8
valid_lft 475sec preferred_lft 475sec
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:6c:c6:1a:3c brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 が表示されなくなりました。IPv6 の無効化については以上です。
tcpdumpのインストール
前回は、Wireshark を使いましたが、コマンドラインで確認するために、tcpdump もインストールしておきます。
$ sudo apt install tcpdump
tcpdump の使い方は、root 権限が必要なので、sudo を付けて実行します。
また、対象とするネットワークインターフェースを -i オプションで指定します。
これでパケットを確認することができますが、見やすくするために、いくつかオプションを使います。
オプション |
説明 |
-n |
ホストアドレスを名前に変換しない |
-nn |
ホストアドレスに加えて、ポート番号も名前に変換しない |
-t |
タイムスタンプを表示しない |
-n と -nn は、実際に使ってみて、違いが分かりませんでした。
ここでは、-n と -t を使います。
VirtualBox + Ubuntu 22.04 のインターネットに繋がるインタフェース(enp0s3)で tcpdump を起動しておき、QEMU から http://www.google.co.jp
に wget コマンドを実行したところをキャプチャしてみます。
まず、QEMU 側です。wget コマンドの -S オプションは、サーバ側のレスポンスを表示します。
Connecting to www.google.co.jp (142.251.222.35:80)
HTTP/1.1 200 OK
Date: Mon, 15 Jul 2024 03:42:07 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=Shift_JIS
Content-Security-Policy-Report-Only: object-src 'none';base-uri 'self';script-src 'nonce-zMcYJVB4lYrRFDRhLYbOHQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: AEC=AVYB7codc8r_4hcLjRGK8ssLHyqdH-1AI-Dpk-JGz4vq9sNl1vihwclMgQ; expires=Sat, 11-Jan-2025 03:42:07 GMT; path=/; domain=.google.co.jp; Secure; HttpOnly; SameSite=lax
Set-Cookie: NID=515=mBDHsj1Jrb6pnJeOb602tO0agFHDUcXdJ3ZMg9kGbp_K7XiJaWr17yawIkAj-Aczp6tsj3kIhBYVuMu34KBWF1Bkb-m-9odbcyF5o228MjIXPCYMfCTH0wTRXGYcTOmyPh17HRoLNp-4qRWcObcyxLyTPLy_L-4Uo7mNUajmRh8; expires=Tue, 14-Jan-2025 03:42:07 GMT; path=/; domain=.google.co.jp; HttpOnly
Accept-Ranges: none
Vary: Accept-Encoding
Connection: close
Transfer-Encoding: chunked
wget: can't open 'index.html': File exists
Date 情報が確認できます。この情報で時刻合わせが出来たりします。
次に、VirtualBox + Ubuntu 22.04 側で、インターネットに繋がるインタフェースを tcpdump を実行した内容です。
$ sudo tcpdump -tn -i enp0s3
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), snapshot length 262144 bytes
IP 10.0.2.15.44635 > 192.168.3.1.53: 43604+ [1au] A? www.google.co.jp. (45)
IP 10.0.2.15.40942 > 192.168.3.1.53: 35084+ [1au] AAAA? www.google.co.jp. (45)
IP 192.168.3.1.53 > 10.0.2.15.44635: 43604 1/4/9 A 172.217.174.99 (319)
IP 192.168.3.1.53 > 10.0.2.15.40942: 35084 1/4/9 AAAA 2404:6800:4004:80c::2003 (331)
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [S], seq 2027278432, win 64240, options [mss 1460,sackOK,TS val 3004422288 ecr 0,nop,wscale 7], length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [S.], seq 14400001, ack 2027278433, win 65535, options [mss 1460], length 0
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 1, win 64240, length 0
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [P.], seq 1:80, ack 1, win 64240, length 79: HTTP: GET / HTTP/1.1
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [.], ack 80, win 65535, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [.], seq 1:2921, ack 80, win 65535, length 2920: HTTP: HTTP/1.1 200 OK
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 2921, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [.], seq 2921:7301, ack 80, win 65535, length 4380: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 7301, win 61320, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [.], seq 7301:13141, ack 80, win 65535, length 5840: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 13141, win 61320, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [P.], seq 13141:14109, ack 80, win 65535, length 968: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 14109, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [P.], seq 14109:16933, ack 80, win 65535, length 2824: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 16933, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [P.], seq 16933:19757, ack 80, win 65535, length 2824: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 19757, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [P.], seq 19757:21581, ack 80, win 65535, length 1824: HTTP
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [.], ack 21581, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [F.], seq 21581, ack 80, win 65535, length 0
IP 10.0.2.15.36010 > 172.217.174.99.80: Flags [F.], seq 80, ack 21582, win 62780, length 0
IP 172.217.174.99.80 > 10.0.2.15.36010: Flags [.], ack 81, win 65535, length 0
^C
26 packets captured
26 packets received by filter
0 packets dropped by kernel
先頭で、http://www.google.co.jp
を DNS で問い合わせて、IP アドレス 172.217.174.99
を取得しています。
その後は、取得した IP アドレスを使って、http プロトコルで、データ(index.html)を取得しています。
tcpdump の使い方は以上になります。
QEMUのネットワークの確認
まず、IPアドレスと、ルーティングテーブルを確認します。
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop qlen 1000
link/ether f2:22:5f:2c:0a:eb brd ff:ff:ff:ff:ff:ff
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fec0::5054:ff:fe12:3456/64 scope site dynamic flags 100
valid_lft 86114sec preferred_lft 14114sec
inet6 fe80::5054:ff:fe12:3456/64 scope link
valid_lft forever preferred_lft forever
4: sit0@NONE: <NOARP> mtu 1480 qdisc noop qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 0 0 0 eth0
10.0.2.0 * 255.255.255.0 U 0 0 0 eth0
続いて、外部とのネットワークの接続を確認します。
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=255 time=11.169 ms
64 bytes from 8.8.8.8: seq=1 ttl=255 time=6.995 ms
^C
--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 6.995/9.082/11.169 ms
PING google.com (172.217.174.110): 56 data bytes
64 bytes from 172.217.174.110: seq=0 ttl=255 time=15.283 ms
64 bytes from 172.217.174.110: seq=1 ttl=255 time=14.880 ms
^C
--- google.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 14.880/15.081/15.283 ms
問題なく、インターネットに繋がることと、DNS による名前解決が行われていることが確認できました。
routeコマンドの _gateway は、具体的なアドレスが知りたいですね。
PING _gateway (10.0.2.2): 56 data bytes
64 bytes from 10.0.2.2: seq=0 ttl=255 time=0.533 ms
64 bytes from 10.0.2.2: seq=1 ttl=255 time=0.480 ms
^C
--- _gateway ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.480/0.506/0.533 ms
なるほど、10.0.2.2 でした。
VirtualBox と同じネットワークアドレスなので、とてもややこしいです。
VirtualBox では、ルーターが 10.0.2.2 で、起動した Ubuntu 22.04 の IPアドレスが 10.0.2.15 です。
一方、QEMU では、同じく、ルーターが 10.0.2.2 で、起動した Buildroot の IPアドレスが 10.0.2.15 です。
IPアドレスが更新されたりしないかな?と思って、DHCPクライアントで、IPアドレスを削除してみました。
udhcpc: started, v1.36.1
udhcpc: broadcasting discover
udhcpc: broadcasting select for 10.0.2.15, server 10.0.2.2
udhcpc: lease of 10.0.2.15 obtained from 10.0.2.2, lease time 86400
deleting routers
adding dns 10.0.2.3
リリースのはずですが、すぐに再取得されました。
しかし、おかげで、QEMU のネットワークの情報が分かりました。
ルーターは 10.0.2.2 は合ってますが、DNS サーバが 10.0.2.3 のようです。
1 /bin/busybox 0 /dev/console
1 /bin/busybox 1 /dev/console
1 /bin/busybox 2 /dev/console
51 /bin/busybox 0 socket:[41]
51 /bin/busybox 1 /dev/null
51 /bin/busybox 2 /dev/null
51 /bin/busybox 3 /tmp/messages
55 /bin/busybox 0 /dev/null
55 /bin/busybox 1 /dev/null
55 /bin/busybox 2 /dev/null
55 /bin/busybox 3 socket:[42]
95 /bin/busybox 0 /dev/null
95 /bin/busybox 1 /dev/null
95 /bin/busybox 2 /dev/null
95 /bin/busybox 3 pipe:[60]
95 /bin/busybox 4 pipe:[60]
97 /bin/busybox 0 /dev/console
97 /bin/busybox 1 /dev/console
97 /bin/busybox 2 /dev/console
97 /bin/busybox 10 /dev/tty
/usr/bin/lsof
lrwxrwxrwx 1 root root 17 Jul 13 09:09 /usr/bin/lsof -> ../../bin/busybox*
Buildroot では、普通の lsof コマンドは持ってなくて、BusyBox が持っている簡易的な lsof コマンドのようです。
よって、PID、実行ファイルパス、オープンしたファイルぐらいしか表示されません。
listen してるポートとそれをオープンしたファイルが知りたかったのですが、BusyBox の lsofコマンドでは難しいようです。
lsofコマンドをインストールする
では、lsofコマンドのソースコードをダウンロードしてきて、クロスコンパイルして、Buildroot で使えるようにしてみたいと思います。
lsofコマンドの GitHub のリリースページです。
github.com
最新バージョンは、lsof 4.99.3 のようです。
ではダウンロードします。
$ wget https://github.com/lsof-org/lsof/archive/refs/tags/4.99.3.tar.gz
$ tar zxvf 4.99.3.tar.gz
$ cd lsof-4.99.3/
続いて、コンフィグを行う必要がありそうですが、まずは、INSTALL ファイルを軽く眺めてみます。
うーん、configure を行ってください、と書かれてありますが、ファイル名が Configure(先頭が大文字)のファイルしかありません。怪しいな、、と思ったので、00README というファイルを見てみます。こちらは、Configure を実行しなさい、と書かれています。
Configure linux と実行すれば良さそうです。
lsofコマンドをネイティブでコンパイルする
まずは、ネイティブでビルド(x86)で試して、うまくいったら、クロスコンパイルをやっていきます。
$ ./Configure linux
実行すると、いくつか質問されますが、よく分からないので、とりあえず、全てリターンを押しました。
Makefile が出来たようなので、あとは make すればいいはずです。
$ make -j $(nproc)
無事に実行ファイル(lsof)が作られたようですので、試します。
$ ./lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
ssh 5768 daisuke 3u IPv4 41976 0t0 TCP daisuke-VirtualBox:55128->192.168.4.1:ssh (ESTABLISHED)
(省略)
Ubuntu に既に入ってる方の lsof の出力と同じような感じだったので、大丈夫そうです。
lsofコマンドをクロスコンパイルする
次は、クロスコンパイルしていきます。
Web で情報を探したり、ChatGPT に聞いてみたのですが、Makefile の変数を直接書き換える方法しかなさそうです。
Makefile の先頭に、CC=cc とありますので、ここをクロスコンパイラに置き換えます。
-CC= cc
+CC= arm-linux-gnueabi-gcc
$ make clean
$ make -j $(nproc)
(省略)
/usr/lib/gcc-cross/arm-linux-gnueabi/11/../../../../arm-linux-gnueabi/bin/ld: ./lib/liblsof.a: error adding symbols: file format not recognized
collect2: error: ld returned 1 exit status
make: *** [Makefile:64: lsof] エラー 1
make が出力した内容を見ると、lib フォルダに移動してからは、クロスコンパイラが使われていませんでした。
ということで、lib フォルダの Makefile も書き換えます。
-CC= cc
+CC= arm-linux-gnueabi-gcc
-AR= ar cr ${LIB} ${OBJ}
+AR= arm-linux-gnueabi-ar cr ${LIB} ${OBJ}
-RANLIB= ranlib ${LIB}
+RANLIB= arm-linux-gnueabi-ranlib ${LIB}
では、make します。
$ make clean
$ make -j $(nproc)
(省略)
arm-linux-gnueabi-gcc -o lsof dfile.o dmnt.o dnode.o dprint.o dproc.o dsock.o dstore.o arg.o main.o print.o store.o usage.o util.o -L./lib -llsof -ltirpc -lselinux
/usr/lib/gcc-cross/arm-linux-gnueabi/11/../../../../arm-linux-gnueabi/bin/ld: -ltirpc が見つかりません: そのようなファイル やディレクトリはありません
/usr/lib/gcc-cross/arm-linux-gnueabi/11/../../../../arm-linux-gnueabi/bin/ld: -lselinux が見つかりません: そのようなファイ ルやディレクトリはありません
collect2: error: ld returned 1 exit status
make: *** [Makefile:64: lsof] エラー 1
クロスコンパイラ用の libtirpc.so と libselinux.so が見つからない、というエラーのようです。
Ubuntu には、他のアーキテクチャのライブラリも apt でインストールすることが出来るようです。
流れとしては、まず、アーキテクチャを追加します。その後、/etc/apt/sources.list を編集して、apt update でエラーが出ないようにします。そこまで出来たら、必要なライブラリをインストールします。
まず、現在のデフォルトのアーキテクチャを確認します。
$ dpkg --print-architecture
amd64
PC の 64bit と設定されていますね。
次に、追加したアーキテクチャを確認します。
$ dpkg --print-foreign-architectures
i386
PC の 32bit が設定されていました。
そこに、今回のクロスコンパイラ(32bit ARM)の設定を追加します。
$ sudo dpkg --add-architecture armhf
$ dpkg --print-foreign-architectures
i386
armhf
追加することが出来ました。
ここで、apt update を行うと、エラーが出ました。
/etc/apt/sources.list の編集が必要なようです。
現在の sources.list の内容です(コメントは削除しました)。
$ cat /etc/apt/sources.list
deb http://jp.archive.ubuntu.com/ubuntu/ jammy main restricted
deb http://jp.archive.ubuntu.com/ubuntu/ jammy-updates main restricted
deb http://jp.archive.ubuntu.com/ubuntu/ jammy universe
deb http://jp.archive.ubuntu.com/ubuntu/ jammy-updates universe
deb http://jp.archive.ubuntu.com/ubuntu/ jammy multiverse
deb http://jp.archive.ubuntu.com/ubuntu/ jammy-updates multiverse
deb http://jp.archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu jammy-security main restricted
deb http://security.ubuntu.com/ubuntu jammy-security universe
deb http://security.ubuntu.com/ubuntu jammy-security multiverse
この状態だと、全行について、現在の全てのアーキテクチャを探そうとします。しかし、今回追加した armhf は別の URL なので、apt update で、エラーが出てしまいます。
そこで、各行について、適用するアーキテクチャを指定するように変更が必要なようです。
また、armhf 用の URL を追加します。
$ sudo nano /etc/apt/sources.list
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy main restricted
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy-updates main restricted
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy universe
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy-updates universe
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy multiverse
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy-updates multiverse
deb [arch=amd64,i386] http://jp.archive.ubuntu.com/ubuntu/ jammy-backports main restricted universe multiverse
deb [arch=amd64,i386] http://security.ubuntu.com/ubuntu jammy-security main restricted
deb [arch=amd64,i386] http://security.ubuntu.com/ubuntu jammy-security universe
deb [arch=amd64,i386] http://security.ubuntu.com/ubuntu jammy-security multiverse
deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse
deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse
deb [arch=armhf] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted universe multiverse
こうすることで、apt update がエラーなく実行できるようになりました。
では、ようやく、必要なライブラリをインストールしてみます。コロンの後にアーキテクチャを指定します。
$ sudo apt-get install libtirpc-dev:armhf libselinux1-dev:armhf
$ find /usr/ -name 'libtirpc*'
/usr/lib/x86_64-linux-gnu/libtirpc.so.3.0.0
/usr/lib/x86_64-linux-gnu/libtirpc.so
/usr/lib/x86_64-linux-gnu/pkgconfig/libtirpc.pc
/usr/lib/x86_64-linux-gnu/libtirpc.so.3
/usr/lib/x86_64-linux-gnu/libtirpc.a
/usr/lib/arm-linux-gnueabihf/libtirpc.so.3.0.0
/usr/lib/arm-linux-gnueabihf/libtirpc.so
/usr/lib/arm-linux-gnueabihf/pkgconfig/libtirpc.pc
/usr/lib/arm-linux-gnueabihf/libtirpc.so.3
/usr/lib/arm-linux-gnueabihf/libtirpc.a
$ find /usr/ -name 'libselinux*'
/usr/share/doc/libselinux1-dev
/usr/share/doc/libselinux1
!/usr/lib/x86_64-linux-gnu/libselinux.so.1
/usr/lib/x86_64-linux-gnu/libselinux.a
/usr/lib/x86_64-linux-gnu/libselinux.so
/usr/lib/x86_64-linux-gnu/pkgconfig/libselinux.pc
/usr/lib/arm-linux-gnueabihf/libselinux.so.1
/usr/lib/arm-linux-gnueabihf/libselinux.a
/usr/lib/arm-linux-gnueabihf/libselinux.so
/usr/lib/arm-linux-gnueabihf/pkgconfig/libselinux.pc
/usr 以下を検索すると、ちゃんとインストールしたライブラリが確認できました(長いので一部省略しました)。
それでは、このライブラリを使うように Makefile を変更します。
まず、トップディレクトリの Makefile です。
-CC= cc
+CC= arm-linux-gnueabihf-gcc
次に、lib ディレクトリの Makefile です。
-CC= cc
+CC= arm-linux-gnueabihf-gcc
-AR= ar cr ${LIB} ${OBJ}
+AR= arm-linux-gnueabihf-ar cr ${LIB} ${OBJ}
-RANLIB= ranlib ${LIB}
+RANLIB= arm-linux-gnueabihf-ranlib ${LIB}
make してみると、先ほどのエラーは無くなり、ビルドが完了しました。
早速、Buildroot に転送して実行してみます。
./lsof: error while loading shared libraries: libselinux.so.1: cannot open shared object file: No such file or directory
エラーになりました。クロスコンパイラ環境にはインストールしましたが、実行環境にライブラリが無いということだと思います。
とりあえず、インストールした2つのライブラリの .so を Buildroot の lsof と同じ場所に転送して、実行してみます。
./lsof: error while loading shared libraries: libpcre2-8.so.0: cannot open shared object file: No such file or directory
うーん、ダメです。他のライブラリが無いということでしょうか。
ダメもとで、/usr/lib/arm-linux-gnueabihf/libpcre2-8.so.0 も Buildroot に転送して、実行してみると、次は、libc のバージョンが対応していない(クロス環境が新しい)というエラーが出ました。
仕方ないので、Ubuntu 22.04 から Ubuntu 18.04 に戻して、試してみます。
Ubuntu で、apt で入るクロスコンパイラって以下の2つがありますね。違いが分かっていません。
- crossbuild-essential-armhf:
dpkg --add-architecture armhf
を実施した場合に、よく紹介されているクロスコンパイラ
- gcc-arm-linux-gnueabihf(インストールするときは g++-arm-linux-gnueabihf を指定した方がいいらしい):検索すると、こちらの方がよくヒットする
今までは何も考えずに後者を使ってましたが、今回は前者を使ってみます。
$ sudo apt install crossbuild-essential-armhf
以下の追加パッケージがインストールされます:
binutils binutils-arm-linux-gnueabihf binutils-common binutils-x86-64-linux-gnu cpp-7-arm-linux-gnueabihf
cpp-arm-linux-gnueabihf dpkg-cross g++-7-arm-linux-gnueabihf g++-arm-linux-gnueabihf gcc-7-arm-linux-gnueabihf
gcc-7-arm-linux-gnueabihf-base gcc-7-cross-base gcc-8-cross-base gcc-arm-linux-gnueabihf libasan4-armhf-cross
libatomic1-armhf-cross libbinutils libc6-armhf-cross libc6-dev-armhf-cross libcilkrts5-armhf-cross libconfig-auto-perl
libconfig-inifiles-perl libdebian-dpkgcross-perl libfile-homedir-perl libfile-which-perl libgcc-7-dev-armhf-cross
libgcc1-armhf-cross libgomp1-armhf-cross libstdc++-7-dev-armhf-cross libstdc++6-armhf-cross libubsan0-armhf-cross
libyaml-perl linux-libc-dev-armhf-cross
よく見ると、依存パッケージとして、gcc-arm-linux-gnueabihf が入っていました。ついでに、g++-arm-linux-gnueabihf も入っています。今度からは、crossbuild-essential-armhf(64bitの場合は crossbuild-essential-arm64)を入れることにします。
では、同様に、追加のライブラリをインストールし、コンフィグで linux を指定し、Makefile を同様に書き換えてビルドします。動的リンクなら問題なく完了しました。
一応、glibc のバージョンを確認します。
$ dpkg -l | grep libc6
ii libc6:amd64 2.27-3ubuntu1.6 amd64 GNU C Library: Shared libraries
ii libc6:armhf 2.27-3ubuntu1.6 armhf GNU C Library: Shared libraries
ii libc6-armhf-cross 2.27-3ubuntu1cross1.1 all GNU C Library: Shared libraries (for cross-compiling)
ii libc6-dbg:amd64 2.27-3ubuntu1.6 amd64 GNU C Library: detached debugging symbols
ii libc6-dev:amd64 2.27-3ubuntu1.6 amd64 GNU C Library: Development Libraries and Header Files
ii libc6-dev:armhf 2.27-3ubuntu1.6 armhf GNU C Library: Development Libraries and Header Files
ii libc6-dev-armhf-cross 2.27-3ubuntu1cross1.1 all GNU C Library: Development Libraries and Header Files (for cross-compiling)
だいぶ古いので大丈夫な気がします。
では、lsof のディレクトリと、クロスコンパイラのライブラリを圧縮して、Ubuntu 22.04 にコピーします。
Buildroot に転送して、実行してみました。動きました!
必要なツールをクロスコンパイルして Buildroot で動かすことは、なかなか難しいということが分かりました。
おわりに
今回は、QEMU のネットワークの調査とその準備、必要なツールのクロスコンパイルを行いました。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。