lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

PQI Air Pen向けにmips-linux-gnu-gccをソースビルドした話

事の発端

年末にNTT-XストアでPQI Air Penが破格の値段でした。
これ、有線/無線でFTPサーバーになるWi-Fiアダプタなのですが、なにより、弄り倒し甲斐のあるLinuxが入ったMIPSマイコンボードな訳です。
nttxstore.jp

そして心強いmoyashi兄さんのブログ
hitoriblog.com

もう買うしかない。正月休みに遊ぶネタが出来たわけです。
飽きてもこれだったら部屋のWiFiルータにはなります。
f:id:Lynx-EyED:20170113134645p:plain

で、クロスコンパイラはSourcery CodeBench Lite Edition for MIPS GNU/Linuxがあるので別に必要ないのですが、

Ubuntu xenial(16.04LTS)はgcc-5-mips-linux-gnuが提供されているのでこれでビルドし、PQI Air Penで動作させようとすると、

~ #./a.out

FATAL: kernel too old
Bus error

~ #

致命的エラー:カーネルおじさんめっちゃ老けてるぞ

よーし、一人だけgcc野良ビルドする苦行2017選手権やるか
正月休みにおすすめしたいとても有益な時間の浪費方法です。絶対やめた方がいい。おすすめです。
Rなんとか社のRXマイコンの時は会社でも家でも散々野良rx-gccビルド大会してましたね。このブログのアクセス解析が某軽子坂からのアクセスだらけになってたのはいい思い出です。あんな共用体地獄ライブラリ付きうんこマイコン金輪際ビルドしてやらねぇ。


息をするように野良ビルドするのも考え物ですがカーネルバージョンに合わせて自分でビルドできるので、時には必要な事もあるのかも。
まず、PQI Air Pen(ファームウェアはv0.1.24に変更済み)のカーネルを確認します

~ # cat /proc/version 
Linux version 2.6.31.AirPen_V0.1.22-g5eca71a (dio@dio) (gcc version 4.3.3 (GCC) ) #324 Thu Feb 7 17:36:23 CST 2013
~ # 

なので、Kernel2.6.31か、それより以前のカーネルヘッダでクロスコンパイラを作った方が良さそうです

mips-linux-gnuのビルド

ホスト
ビルドおよび動作確認したのは以下の環境です

ubuntuはメインで使っている14.04で動くことを目指しました。(結局16.04でもビルドできた。詳細後述)
16.04LTSいろいろ難しい…

どちらも前もってインストールしておくパッケージ

$ sudo apt-get update
$ sudo apt-get install build-essential m4 gawk g++ make


GCCビルド成功した組み合わせ

GMP,MPC,MPFRを別個にwgetしてもいいのですがGCCフォルダ内の

contrib/download_prerequisites

を起動して自動取得しています。

Ubuntu 16.04LTSがbinutilsのビルドからこけやがる件

本当は根本的な解決をするべきなのでしょうが、よくわからないところで警告出してエラー終了しているので-Wno-errorで回避していまいます。

CFLAGS=-Wno-error
binutils-${BINUTILSVERSION}/configure (中略) --disable-warnings-as-errors

スクリプトかいた

出来てしまうと大したことはないのですが、すごくめんどくさいのと大したことないのは別問題なので(?)スクリプト書きました。めっちゃ適当に書いてるので、詳しい人書き直して。
ビルド前にmips-linux-gnuをすでにインストールしている場合はアンインストールするか、$PATHをunsetしておきましょう

#!/bin/bash -ev

# see https://www.linux-mips.org/wiki/Toolchains#Roll-your-own
# see http://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/
# see also http://www.hs-augsburg.de/~beckmanf/dokuwiki/doku.php?id=mips_cross_compiler

export WDIR=$HOME/tmp
export TARGET=mips-linux-gnu
export PREFIX=$HOME/mips-cross
export TARCH=mips
export PATH="${PATH}":${PREFIX}/bin

# ------------------------------
# enviroments
# ------------------------------
echo ${WDIR}
echo ${TARGET}
echo ${PREFIX}
echo ${PATH}
# ------------------------------
mkdir -p ${PREFIX}
mkdir -p $WDIR
cd $WDIR
mkdir -p ${TARGET}-toolchain 
cd ${TARGET}-toolchain


# version
BINUTILSVERSION="2.24"
GCCVERSION="4.9.2"
#GDBVERSION="7.7"
ISLVERSION="0.12.2"
CLOOGVERSION="0.18.1"
#GLIBCVERSION="2.20"
#PORTSVERSION="2.20"
GLIBCVERSION="2.19"
PORTSVERSION="2.19"
#KERNELVERSION="3.17.2"
#KERNELVERSION="2.6.27.62"
#KERNELVERSION="2.6.32.60"
KERNELVERSION="2.6.30"


# wget
if [ ! -e "binutils-${BINUTILSVERSION}.tar.bz2" ] ; then
wget http://ftp.gnu.org/gnu/binutils/binutils-${BINUTILSVERSION}.tar.bz2
fi

if [ ! -e "gcc-${GCCVERSION}.tar.bz2" ] ; then
wget http://ftp.gnu.org/gnu/gcc/gcc-${GCCVERSION}/gcc-${GCCVERSION}.tar.bz2
fi

<< COMMENTOUT
if [ ! -e "gdb-${GDBVERSION}.tar.bz2" ] ; then
wget http://ftp.gnu.org/gnu/gdb/gdb-${GDBVERSION}.tar.bz2
fi
COMMENTOUT

if [ ! -e "linux-${KERNELVERSION}.tar.xz" ] ; then
# wget https://www.kernel.org/pub/linux/kernel/v2.6/longterm/v2.6.27/linux-${KERNELVERSION}.tar.xz
# wget https://www.kernel.org/pub/linux/kernel/v2.6/longterm/v2.6.32/linux-${KERNELVERSION}.tar.xz
# wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-${KERNELVERSION}.tar.xz
wget https://www.kernel.org/pub/linux/kernel/v2.6/linux-${KERNELVERSION}.tar.xz
fi

# ----------------------
# wget kernel-headers
# ----------------------
if [ ! -d "linux-${KERNELVERSION}" ] ; then
tar -Jxvf linux-${KERNELVERSION}.tar.xz
fi

# ----------------------
# binutils
# ----------------------
if [ ! -d "binutils-${BINUTILSVERSION}" ] ; then
tar xjf binutils-${BINUTILSVERSION}.tar.bz2
fi


# ----------------------
# gcc
# ----------------------
if [ ! -d "gcc-${GCCVERSION}" ] ; then
tar xjf gcc-${GCCVERSION}.tar.bz2
fi

#-----------------------
# get infrastructure
# gmp, mpfr, gmp
#-----------------------
if [ ! -d "gmp" ] ; then
gcc-${GCCVERSION}/contrib/download_prerequisites
fi

# get isl
if [ ! -e "isl-${ISLVERSION}.tar.bz2" ] ; then
  wget  ftp://gcc.gnu.org/pub/gcc/infrastructure/isl-${ISLVERSION}.tar.bz2
fi

if [ ! -d "isl-${ISLVERSION}" ] ; then
tar -xvjf isl-${ISLVERSION}.tar.bz2
fi

# get cloog
if [ ! -e "cloog-${CLOOGVERSION}.tar.gz" ] ; then
wget ftp://gcc.gnu.org/pub/gcc/infrastructure/cloog-${CLOOGVERSION}.tar.gz
fi

if [ ! -d "cloog-${CLOOGVERSION}" ] ; then
tar -xvf cloog-${CLOOGVERSION}.tar.gz
fi

# get glibc
if [ ! -e "glibc-${GLIBCVERSION}.tar.xz" ] ; then
wget ftp://ftp.gnu.org/gnu/glibc/glibc-${GLIBCVERSION}.tar.xz
fi


if [ ! -d "glibc-${GLIBCVERSION}" ] ; then
tar -Jxvf glibc-${GLIBCVERSION}.tar.xz
fi
<< COMMENTOUT
# get glibc-ports
if [ ! -e "glibc-ports-${PORTSVERSION}.tar.bz2" ] ; then
wget http://ftp.gnu.org/gnu/libc/glibc-ports-${PORTSVERSION}.tar.bz2
fi

if [ ! -d "glibc-ports-${PORTSVERSION}" ] ; then
tar -xvjf glibc-ports-${PORTSVERSION}.tar.bz2
fi
COMMENTOUT

#ln -sf glibc-ports-${PORTSVERSION} glibc-${GLIBCVERSION}/ports  
# cd glibc-${GLIBCVERSION}
# ln -sf ../glibc-ports-${GLIBCVERSION} ports
# cd ..
# --------------------------------------------------------
# Build step 0: make symbolic link
# 				mpfr, gmp, mpc, mpfr, isl, cloog 
# --------------------------------------------------------
cd gcc-${GCCVERSION}
ln -sf ../mpfr mpfr
ln -sf ../gmp gmp
ln -sf ../mpc mpc
ln -sf ../isl-${ISLVERSION} isl
ln -sf ../cloog-${CLOOGVERSION} cloog
cd ..

# --------------------------------------------------------
# Build step 1: make Binutils
# 		configure & make & install 
# --------------------------------------------------------
mkdir -p build-binutils
cd build-binutils

if [ ! -e "config.status" ] ; then
CFLAGS=-Wno-error ../binutils-${BINUTILSVERSION}/configure --target=${TARGET} --prefix=${PREFIX} --disable-warnings-as-errors
fi

if [ ! -e "${PREFIX}/bin/${TARGET}-ld" ] ; then
make
make install
fi

cd ..

# --------------------------------------------------------
# Build step 2: make Linux Kernel Headers
# 		make headers_install 
# --------------------------------------------------------
cd linux-${KERNELVERSION}
make ARCH=${TARCH} INSTALL_HDR_PATH=${PREFIX}/${TARGET} headers_install
cd ..

# --------------------------------------------------------
# Build step 3: Build GCC but libraries
# 		make all-gcc && make install-gcc
# --------------------------------------------------------
mkdir -p build-gcc
cd build-gcc
../gcc-${GCCVERSION}/configure --prefix=${PREFIX} --target=${TARGET} --enable-languages=c,c++ --disable-multilib
make all-gcc
make install-gcc
cd ..
# --------------------------------------------------------
# Build step 4: Standard C library Headers and Startup Files
# 		make (removed --enable-add-ons=ports,  --disable-sanity-checks--with-tls --disable-versioning --disable-profile)
# --------------------------------------------------------
mkdir -p build-glibc
cd build-glibc
../glibc-${GLIBCVERSION}/configure --prefix=${PREFIX}/${TARGET} --build=${MACHTYPE} --host=${TARGET} --target=${TARGET} --with-headers=${PREFIX}/${TARGET}/include --disable-multilib libc_cv_forced_unwind=yes
make install-bootstrap-headers=yes install-headers
make csu/subdir_lib
install csu/crt1.o csu/crti.o csu/crtn.o ${PREFIX}/${TARGET}/lib
${TARGET}-gcc -nostdlib -nostartfiles -shared -x c /dev/null -o ${PREFIX}/${TARGET}/lib/libc.so
touch ${PREFIX}/${TARGET}/include/gnu/stubs.h
cd ..
# --------------------------------------------------------
# Build step 5: Compiler Support library 
# 		make 
# --------------------------------------------------------
cd build-gcc
make all-target-libgcc
make install-target-libgcc
cd ..

# --------------------------------------------------------
# Build step 6: Standard C library 
# 		make 
# --------------------------------------------------------
cd build-glibc
make 
make install
cd ..
# --------------------------------------------------------
# Build step 7: Standard C++ library 
# 		make 
# --------------------------------------------------------
cd build-gcc
make 
make install
cd ..

ビルドが無事終わったら忘れないうちに.bashrcにPATHを通しておいた方がいいと思います。PCはCore-i5 5200Uが乗っているWindows7ノートでやったのですが、
ビルド時間はubuntuのパッケージバージョンには関係なく
VMware環境 2時間半
デュアル環境 40分
でした。仮想環境結構遅いんだね。

#include <stdio.h>
int main(int argc, char *argv[]){

	printf("hello world.\nThis is PQI Air Pen.\n");
	return 0;

}

という何の変哲もないコードをhello.cなどと保存し、

$ mips-linux-gnu-gcc -g -O2 -Wall -pedantic -march=24kc -static -o hello hello.c

とすれば、3MB以上もある巨大バイナリが完成する。(staticビルドだからしかたない)
これをmicroSDにコピーするか、FTP経由でPQI Air Penにアップロードする。
telnetPQI Air Penにログイン

hoge@ubuntu:~$ telnet 192.168.200.1
Trying 192.168.200.1...
Connected to 192.168.200.1.
Escape character is '^]'.
(none) login: root
Password: (パスワード:pqiap)


BusyBox v1.01 (2013.02.07-09:36+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

~ # cd /tmp/www/ftp/sda1/
/tmp/www/ftp/sda1 # ./hello
 hello world.
 This is PQI Air Pen.

/tmp/www/ftp # 

ncursesをtelnet使ってパイプする方法がわかればslコマンドでも、と思ったのですが、いかんせんよくわからず…
また時間のあるときに、Luaでもビルドしてみよう。

おしまい。


参考サイト

https://www.linux-mips.org/wiki/Toolchains#Roll-your-own MIPSロスコンパイラをビルドするのに最低限必要な内容
http://preshing.com/20141119/how-to-build-a-gcc-cross-compiler/ Linux上で動作するクロスコンパイラをビルドのするのに必要な知識
http://www.hs-augsburg.de/~beckmanf/dokuwiki/doku.php?id=mips_cross_compiler bare-metalなMIPSコンパイラを作る時に必要な知識