自分が大学1年のとき、富士通のFMVがフルセットで20万という破格の値段で売り出されて爆売れした。周りでも買った人が結構多かったのだが、安いには訳がある。なんと486SX2というCPUで浮動小数点ユニットがない。理系の人間からするとこりゃ困ったという状態で、ネットニュースで学内でDXを売ってくれる人探して、学生生協前で取引なんていうことがあった。自分は、486SXというさらに遅いCPU、それもノートなのでCPUが変えられない。Linuxのカーネルのコンパイルに丸一日。当時のLinuxのカーネルは、リコンパイルしないと全然使い物にならない。大学には、SUNやらPentium Proのマシンがゴロゴロあるのに。。。オラにその力をくれー。
今でも、Raspberry PIやルータのEdgeRouter Xなんかのプログラムを作ろうとしたら、当時よりだいぶマシだが、やはりお時間がかかる。手元にはApple Siliconとかあるのに。。。やはり、オラにその力をくれー。
それを解決するのが、クロスコンパイル。チョッ早のApple Siliconがくそ遅いRaspberry PI Zeroの代わりに、Raspberry PI Zeroのバイナリを作ってくれる。しかし、CPUのアーキテクチャは歴史や黒歴史が多いので、さっぱりわからない。毎度、調べて心が折れる。調べてみた。
閑話休題
手元にあるデバイスをすべて網羅した。また、手元にないIBM Powerやs390もリストアップした。実は、手元にハードウェアがなくてもQEMUでエミュレートできる。よって、動作確認方法も記載した。
実は、微妙にめんどくさかったのが、EdgeRouter Xと4/6の環境。Big EndianとLittle Endianの違い、どちらかわからない。さらにFPUがあるかどうかも不明だったので、実機で動作確認した結果なので間違いはないはず。(PowerPC系とメインフレームは実機確認はできていない。)CPUの微妙な違いは面倒。cat /proc/cpuinfoをAIに貼り付けてインストラクションセットレベルでリコメンドしてもらった。
また、クロスコンパイラは、実際には、各自使いたいコンパイラがあると思うが、代表として、C++でリストアップした。(GCCがver 14だって。。。自分の時は、まだver 2だったのに)Dockerの有無も調べておいた。
# linux/386
- Docker: 一部対応 非推奨(古い)、Alpine など軽量ディストリビューションで使用可
- 必要パッケージ: apt -y install g++-multilib-i686-linux-gnu
- コンパイル:i686-linux-gnu-g++ -m32 -march=pentium-mmx -mtune=pentium-mmx -static hello.cpp -o hello_linux_386
- 動作確認: qemu-i386-static -cpu coreduo ./hello_linux_386
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -o go_hello_linux_386 hello.go
- GO動作確認: qemu-i386-static -cpu coreduo ./go_hello_linux_386
# linux/amd64
- Docker: 公式 & 完全対応
- 必要パッケージ: apt -y install g++-multilib-x86-64-linux-gnux32 g++-x86-64-linux-gnu
- コンパイル: x86_64-linux-gnu-g++ -m64 -march=skylake -mtune=skylake -static hello.cpp -o hello_linux_amd64
- 動作確認: qemu-x86_64-static -cpu Skylake-Client ./hello_linux_amd64
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o go_hello_linux_amd64 hello.go
- GO動作確認: qemu-x86_64-static -cpu Skylake-Client ./go_hello_linux_amd64
# linux/armel-v6 (ARMv6 / aarch32 / 32bit / armel / Raspberry Pi Zero)
- Docker: 一部対応 Raspberry Pi Zero 向け、Alpineなど一部対応
- 必要パッケージ: apt -y install g++-arm-linux-gnueabi
- コンパイル: arm-linux-gnueabi-g++ -march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=soft -static hello.cpp -o hello_linux_armel
- 動作確認: qemu-arm-static -cpu arm1176 -L /usr/arm-linux-gnueabi ./hello_linux_armel
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -o go_hello_linux_armel hello.go
- GO動作確認: qemu-arm-static -cpu arm1176 -L /usr/arm-linux-gnueabi ./go_hello_linux_armel
# linux/armhf-v7 (ARMv7 / aarch32 / 32bit / armhf / Raspberry Pi 2 Model B v1.1)
- Docker: 公式あり Raspberry Pi 2/3向け。多くの軽量イメージが対応
- 必要パッケージ: apt -y install g++-arm-linux-gnueabihf
- コンパイル: arm-linux-gnueabihf-g++ -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -static hello.cpp -o hello_linux_armhf
- 動作確認: qemu-arm -cpu cortex-a7 -L /usr/arm-linux-gnueabihf ./hello_linux_armhf
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -o go_hello_linux_armhf hello.go
- GO動作確認: qemu-arm -cpu cortex-a7 -L /usr/arm-linux-gnueabihf ./go_hello_linux_armhf
# linux/arm64 (ARMv8 / aarch64 / 64bit / Raspberry Pi 2 Model B v1.2/3/4/5)
- Docker: 公式あり Raspberry Pi 3/4/5、Apple M1/M2 でも利用可
- 必要パッケージ: apt -y install g++-aarch64-linux-gnu g++
- コンパイル: aarch64-linux-gnu-g++ -march=armv8-a -static hello.cpp -o hello_linux_arm64
- 動作確認: qemu-aarch64 -cpu cortex-a53 -L /usr/aarch64-linux-gnu ./hello_linux_arm64
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o go_hello_linux_arm64 hello.go
- GO動作確認: qemu-aarch64 -cpu cortex-a53 -L /usr/aarch64-linux-gnu ./go_hello_linux_arm64
# linux/mipsle softfloat (EdgeRouter X)
- Docker: 公式なし QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-multilib-mipsel-linux-gnu
- コンパイル: mipsel-linux-gnu-g++ -march=mips32r2 -mtune=1004kc -mhard-float -mabi=32 -EL -mdsp -mno-mips16 -static hello.cpp -o hello_linux_mipsel
- 動作確認:qemu-mipsel -cpu 34Kf ./hello_linux_mipsel
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -o go_hello_linux_mipsel hello.go
- GO動作確認: qemu-mipsel -cpu 34Kf ./go_hello_linux_mipsel
# linux/mips (もうあまりない)
- 必要パッケージ: apt -y install g++-multilib-mips-linux-gnu
- コンパイル: mips-linux-gnu-g++ -mabi=32 -static hello.cpp -o hello_linux_mips
- 動作確認: qemu-mips -cpu 24Kf ./hello_linux_mips
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -o go_hello_linux_mips hello.go
- GO動作確認: qemu-mips -cpu 24Kf ./go_hello_linux_mips
# linux/mips64 (EdgeRouter 4/6)
- Docker: 公式なし QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-multilib-mips64-linux-gnuabi64
- コンパイル: mips64-linux-gnuabi64-g++ -march=octeon3 -mtune=octeon3 -mabi=64 -mhard-float -static hello.cpp -o hello_linux_mips64
- 動作確認: qemu-mips64 -cpu MIPS64R2-generic ./hello_linux_mips64
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -o go_hello_linux_mips64 hello.go
- GO動作確認: qemu-mips64 -cpu MIPS64R2-generic ./go_hello_linux_mips64
# linux/mips64le (Cavium/Marvell ただしEdgeRouterは除外)
- Docker: 公式なし QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-multilib-mips64el-linux-gnuabi64
- コンパイル: mips64el-linux-gnuabi64-g++ -mabi=64 -march=mips64r2 -static hello.cpp -o hello_linux_mips64le
- 動作確認: qemu-mips64el-static -cpu MIPS64R2-generic ./hello_linux_mips64le
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -o go_hello_linux_mips64le hello.go
- GO動作確認: qemu-mips64el-static -cpu MIPS64R2-generic ./go_hello_linux_mips64le
# linux/ppc64 (レガシー IBM Power7)
- Docker: 公式なし QEMUエミュレーションが必要
- Ubuntu ARM64ではC++のコンパイルできない。Goのみ
- 必要パッケージ: apt -y install g++-multilib-powerpc64-linux-gnu
- コンパイル: powerpc64-linux-gnu-g++ -static -O2 -march=power8 -mtune=power8 hello.cpp -o hello_linux_ppc64
- 動作確認: qemu-ppc64-static -cpu POWER8 ./hello_linux_ppc64
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=ppc64 go build -o go_hello_linux_ppc64 hello.go
- GO動作確認: qemu-ppc64-static -cpu POWER8 ./go_hello_linux_ppc64
# linux/ppc64le (IBM Power9/10)
- Docker: 公式なし QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-powerpc64le-linux-gnu
- コンパイル: powerpc64le-linux-gnu-g++ -mcpu=power8 -mtune=power8 -static hello.cpp -o hello_linux_ppc64le
- 動作確認: qemu-ppc64le-static ./hello_linux_ppc64le
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build -o go_hello_linux_ppc64le hello.go
- GO動作確認: qemu-ppc64le-static ./go_hello_linux_ppc64le
# linux/s390x (64bit)
- Docker: 公式なし QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-multilib-s390x-linux-gnu
- コンパイル: s390x-linux-gnu-g++ -static hello.cpp -o hello_linux_s390x
- 動作確認: qemu-s390x-static ./hello_linux_s390x
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=s390x go build -o go_hello_linux_s390x hello.go
- GO動作確認: qemu-s390x-static ./go_hello_linux_s390x
# linux/riscv64 (DevTerm RISC-V)
- Docker:実験的サポート QEMUエミュレーションが必要
- 必要パッケージ: apt -y install g++-riscv64-linux-gnu
- コンパイル: riscv64-linux-gnu-g++ -march=rv64imafdcv -mabi=lp64d -static hello.cpp -o hello_linux_riscv64
- 動作確認: qemu-riscv64 -cpu thead-c906 ./hello_linux_riscv64
- GOコンパイル: CGO_ENABLED=0 GOOS=linux GOARCH=riscv64 go build -o go_hello_linux_riscv64 hello.go
- GO動作確認: qemu-riscv64 -cpu thead-c906 ./go_hello_linux_riscv64
# darwin/amd64
- GOコンパイル: CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o go_hello_darwin_amd64 hello.go
# darwin/arm64 (Apple Silicon)
- GOコンパイル: CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o go_hello_darwin_arm64 hello.go
# solaris/amd64
- GOコンパイル: CGO_ENABLED=0 GOOS=solaris GOARCH=amd64 go build -o go_hello_solaris_amd64 hello.go
# windows/386
- 必要パッケージ: apt -y install g++-mingw-w64-i686
- コンパイル: i686-w64-mingw32-g++ -m32 -march=pentium-mmx -mtune=pentium-mmx hello.cpp -o hello_windows_386.exe
- GOコンパイル: CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -o go_hello_windows_386 hello.go
# windows/amd64
- 必要パッケージ: apt -y install g++-mingw-w64-x86-64
- コンパイル: x86_64-w64-mingw32-g++ -m64 -march=skylake -mtune=skylake hello.cpp -o hello_windows_amd64.exe
- GOコンパイル: CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o go_hello_windows_amd64 hello.go
# windows/arm64
- GOコンパイル: CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -o go_hello_windows_arm64 hello.go
テストに便利なサンプルコード(AIに書かせた)
C++版 hello.cpp
#include <iostream>
#include <chrono>
int main() {
constexpr int iterations = 100000000;
double a = 1.5;
double b = 2.25;
double sum = 0.0;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; ++i) {
sum += a * b + a / (b + 1.0) - b / (a + 0.5);
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end - start;
std::cout << "C++ Double Sum = " << sum << "\n”;
std::cout << "Elapsed time = " << elapsed.count() << " sec\n”;
return 0;
}
Go版 Hello.go
package main
import (
“fmt”
“time”
)
func main() {
const iterations = 100000000
a := 1.5
b := 2.25
sum := 0.0
start := time.Now()
for i := 0; i < iterations; i++ {
sum += a*b + a/(b+1.0) - b/(a+0.5)
}
elapsed := time.Since(start)
fmt.Printf("Go Double Sum = %.6f\n", sum)
fmt.Printf("Elapsed time = %.6f sec\n", elapsed.Seconds())
}
重い浮動小数点の計算をさせているので、Raspberry Pi Zeroだと結果がでるまで2分近くかかる。
シングルコードでマルチアーキテクチャ、OSのバイナリは、Goだけではなく、Pythonでも出力できるのだが、Goのほうが簡単。単にGoをインストールして、コンパイルするだけなので。事前準備もいらないし、気も使わない。最近は、Goしか使っていない。
誰得だかわからないが。。。自分はちょっと役に立ちそう。秋葉原のジャンク屋とかで。。。CPUのアーキテクチャによってはそのアーキテクチャで最速のものでもクソ遅いのもある。そういう時は、この資料の出番。
また、ここにDECのAlphaが無くなっているのは歴史を感じる。整数がチョッ早、浮動小数点が激遅だったCPU。
https://ja.wikipedia.org/wiki/DEC_Alpha
当時のCompaqは、DEC AlphaからItaniumに移行しようとしていた。華やかだったけど最後黒歴史から漆黒の歴史に。。。
ちなみに、以下は完全余談の誰得情報
上記に書かれていないもので、G++のみでバイナリが作成でき動作確認ができるのは
- linux/alpha
- linux/m68k
- linux/powerpc
- linux/sh4
- linux/hppa (HPPA 32bit)
- linux/sparc64
バイナリだけ作成ができて、動作確認には実機が必要 (もはや完全未確認)
- linux/hppa64 (HPPA 64bit) (Cのみで、C++は無し)
- linux/mipsisa32r6(MIPS R6 BE)など
- linux/ia64 (以前はあったのかもしれないが、今はbinutilsしかない)