#navi_header|技術| uClibcをPC環境で試すのに自分が使っている方法を紹介します。 環境: CentOS 5.5 (x86, 32bit) gcc-4.1.2-48.el5 libgcc-4.1.2-48.el5 binutils-2.17.50.0.6-14.el5 uClibc-0.9.31 ''Buildrootは使いません。'' 紹介するサンプルでは以下のディレクトリ構成を使っています。"/home/msakamoto/reduced.linux"の部分は適宜読み替えてください。 /home/msakamoto/reduced.linux/uclibc_test2/ uClibc-0.9.31.tar.bz2 uClibc-0.9.31/ uclibc_test/ Makefile ret1.c helloworld.c その他細かいディレクトリは続いて紹介するMakefile中の変数を適宜調整して対応してください。 #more|| #outline|| ---- * Makefile, ret1.c, helloworld.c Makefile: #pre||> # オリジナルのソースツリー UCLIBC_SRC_DIR=/home/msakamoto/reduced.linux/uClibc-0.9.31 # 実験環境のルートディレクトリ TEST_DIR=/home/msakamoto/reduced.linux/uclibc_test # "make O=..." で指定するビルドディレクトリ UCLIBC_BUILD_DIR=$(TEST_DIR)/build # 開発用のヘッダーとライブラリのインストール先 UCLIBC_DEV_DIR=$(TEST_DIR)/dev # 実行時に使うライブラリとローダ(ld-uClibc)のインストール先 UCLIBC_RUNTIME_DIR=$(TEST_DIR)/runtime # アプリケーションをリンクするときに指定するローダのフルパス UCLIBC_RUNTIME_INTERP=$(UCLIBC_RUNTIME_DIR)/lib/ld-uClibc.so.0 # 必要に応じてprefixを設定 #PREFIX_INSTALL_DEV=PREFIX=$(UCLIBC_DEV_DIR) #PREFIX_INSTALL_RUNTIME=PREFIX=$(UCLIBC_RUNTIME_DIR) # Linuxのヘッダーファイルのディレクトリ(ホストPCの環境をそのまま流用) LINUX_KERNEL_HEADER=/usr/include # 以下、ret1.c, helloworld.c のコンパイルとリンク用オプション設定 CFLAGS=-nostdinc \ -I$(UCLIBC_DEV_DIR)/usr/include \ -I$(LINUX_KERNEL_HEADER) \ -Wall \ -D_GNU_SOURCE \ -isystem /usr/lib/gcc/i386-redhat-linux/4.1.2/include-fixed \ -isystem /usr/lib/gcc/i386-redhat-linux/4.1.2/include \ -fno-builtin LFLAGS_COMMON=-nostdlib \ -L $(UCLIBC_RUNTIME_DIR)/lib \ -L $(UCLIBC_DEV_DIR)/usr/lib \ -Wl,--dynamic-linker=$(UCLIBC_RUNTIME_INTERP) \ CRT_START=$(UCLIBC_DEV_DIR)/usr/lib/crt1.o \ $(UCLIBC_DEV_DIR)/usr/lib/crti.o CRT_STATIC=$(UCLIBC_DEV_DIR)/usr/lib/libc.a \ $(UCLIBC_DEV_DIR)/usr/lib/uclibc_nonshared.a CRT_END=$(UCLIBC_DEV_DIR)/usr/lib/crtn.o TARGETS=ret1_static ret1_dyn helloworld_static helloworld_dyn help: @echo "targets:" @echo " uclibc-menuconfig" @echo " uclibc-build" @echo " uclibc-clean" @echo " testbuild ($(TARGETS))" @echo " clean (remove *.o and $(TARGETS))" # 以下、uClibcのmenuconfig, all, install_dev, install_runtime, cleanターゲットのラッパ uclibc-menuconfig: $(MAKE) -C $(UCLIBC_SRC_DIR) O=$(UCLIBC_BUILD_DIR) menuconfig uclibc-build: $(MAKE) -C $(UCLIBC_SRC_DIR) O=$(UCLIBC_BUILD_DIR) all $(MAKE) -C $(UCLIBC_SRC_DIR) O=$(UCLIBC_BUILD_DIR) $(PREFIX_INSTALL_DEV) install_dev $(MAKE) -C $(UCLIBC_SRC_DIR) O=$(UCLIBC_BUILD_DIR) $(PREFIX_INSTALL_RUNTIME) install_runtime uclibc-clean: $(MAKE) -C $(UCLIBC_SRC_DIR) O=$(UCLIBC_BUILD_DIR) realclean rm -rf $(UCLIBC_DEV_DIR) rm -rf $(UCLIBC_RUNTIME_DIR) # 以下、ret1.c, helloworld.c 関連のターゲット設定 .SUFFIXES: .c .o .c.o: $(CC) $(CFLAGS) -c -o $@ $< testbuild: $(TARGETS) ret1_static: ret1.o $(CC) -static -o $@ $(LFLAGS_COMMON) $(CRT_START) $< $(CRT_STATIC) $(CRT_END) ret1_dyn: ret1.o $(CC) -lc -o $@ $(LFLAGS_COMMON) $(CRT_START) $< $(CRT_END) helloworld_static: helloworld.o $(CC) -static -o $@ $(LFLAGS_COMMON) $(CRT_START) $< $(CRT_STATIC) $(CRT_END) helloworld_dyn: helloworld.o $(CC) -lc -o $@ $(LFLAGS_COMMON) $(CRT_START) $< $(CRT_END) clean: rm -f *.o rm -f $(TARGETS) ||< ret1.c : シンプルな、とりあえずリンクできて実行出来るか確認するだけのプログラム。 #code|c|> int main(void) { return 1; } ||< helloworld.c : 想定通りのLinuxカーネルヘッダをinclude出来ているか確認できるようちょっと調整。 #code|c|> #include #include int main(int argc, char *argv[]) { int i; printf("Hello, World! LINUX_VERSION_CODE=%ld\n", LINUX_VERSION_CODE); for (i = 0; i < argc; i++) { printf("args[%d]=[%s]\n", i, argv[i]); } return 0; } ||< * uClibcのビルド 1.uClibcのアーカイブを展開する。 2.以下の記事を参照し、 "libc/misc/time/time.c" を修正する。 - uClibc-0.9.31 symbol table oddities -- http://permalink.gmane.org/gmane.comp.lib.uclibc.general/19300 - Discussion and development of uClibc (the embedded C library) -- http://comments.gmane.org/gmane.comp.lib.uclibc.general/19298 - 解决uClibc-0.9.31 can’t resolve symbol ll_tzname_UNKNOWN ? 三人行 -- http://blog.microsuncn.com/?p=1467 3.以下の記事を参照し、"extra/scripts/install_headers.sh" を修正する。 - 技術/Linux/uClibc/01, "make install_headers" 関連PATCH(uClibc-0.9.31) - Glamenv-Septzen.net -- http://www.glamenv-septzen.net/view/945 4."make defconfig"でビルドディレクトリを初期化する。 $ cd uClibc-0.9.31 $ make O=`pwd`/reduced.linux/uclibc_test/build defconfig '' "O=" オプションはフルパスで指定する。 '' 5."make uclibc-menuconfig" で微調整する。 $ cd uclibc_test $ make uclibc-menuconfig + LDSO_GNU_HASH_SUPPORT を有効化する。 [[948]] 参照。 + RUNTIME_PREFIX, DEVEL_PREFIX を適宜調整する。 + HARDWIRED_ABSPATH を無効化する。 サンプル差分diff: #pre||> 91c91 < # LDSO_GNU_HASH_SUPPORT is not set --- > LDSO_GNU_HASH_SUPPORT=y 214,215c214,215 < RUNTIME_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/" < DEVEL_PREFIX="/usr/$(TARGET_ARCH)-linux-uclibc/usr/" --- > RUNTIME_PREFIX="/home/msakamoto/reduced.linux/uclibc_test/runtime" > DEVEL_PREFIX="/home/msakamoto/reduced.linux/uclibc_test/dev/usr" 217c217 < HARDWIRED_ABSPATH=y --- > # HARDWIRED_ABSPATH is not set 235,236c235 < # DODEBUG is not set < DOSTRIP=y --- > DODEBUG=y ||< ''静的リンクしたバイナリが Segmentation Fault してしまう場合は、 DODEBUG オプションを有効化してuClibcおよび対象アプリケーションをリビルドしてみてください。'' 6."make uclibc-build" でビルドする。 $ make uclibc-build 開発用ヘッダー・ライブラリファイルおよび実行時ライブラリがインストールで来たか確認: /home/msakamoto/reduced.linux/uclibc_test/ dev/ usr/ lib/ : *.a, *.so, symbolic-links include/ : *.h # "bits/", "sys/" ディレクトリ以下もコピーされていること。 runtime/ lib/ : *.so, symbolic-links * ret1, helloworld のビルド 7.ret1, helloworldをビルドする。 $ make testbuild 静的リンク版:ret1_static, helloworld_static 動的リンク版:ret1_dyn, helloworld_dyn 動的リンク版の場合は ".interp" セクションが想定通りのローダを指しているか確認: $ readelf -x .interp ret1_dyn Hex dump of section '.interp': 0x080480f4 2f6f746f 6d616b61 736d2f65 6d6f682f /home/msakamoto/ 0x08048104 63752f78 756e696c 2e646563 75646572 reduced.linux/uc 0x08048114 6d69746e 75722f74 7365745f 6362696c libc_test/runtim 0x08048124 2e636269 6c43752d 646c2f62 696c2f65 e/lib/ld-uClibc. 0x08048134 00 302e6f73 so.0. 実行してみます。 $ ./ret1_dyn $ echo $? 1 $ ./helloworld_dyn abc def Hello, World! LINUX_VERSION_CODE=132626 args[0]=[./helloworld_dyn] args[1]=[abc] args[2]=[def] $ ./ret1_static $ echo $? 1 $ ./helloworld_static abc def Hello, World! LINUX_VERSION_CODE=132626 args[0]=[./helloworld_static] args[1]=[abc] args[2]=[def] 8.後片付け ret1, helloworld のバイナリを削除: $ make clean uClibcのバイナリ(*.o含む), "dev/", "runtime/" 以下を削除: $ make uclibc-clean * gccに渡す "crtX.o" オプションを省略するには 上に示したMakefileでは、ret1.c, helloworld.c をリンクする時に明示的に -nostdlib を指定し、Cランタイムルーチンも CRT_START, CRT_END で明示的に指定しています。 しかしBusyBoxなど大きなソースツリーの場合、ここまで細くgccオプションを指定するのが難しくなります。そこで、"-nostdlib" や "crtX.o" を明示的にgccに渡さなくても済む方法を紹介します。なお、今回は静的リンクについては無視します。 基本方針: ''specsファイルのカスタマイズ'' specsファイルとは、ドライバプログラムであるgccの振る舞いを決定するファイルです。Makefileに似た構造のテキストファイルで、アセンブルやリンクなど各タスクにおけるコマンドラインを定義していくことが出来ます。 gcc3以前はデフォルトのspecsがファイルとしてインストールされていたようですが、gcc4以降はプログラム内部に組み込まれたようで、デフォルトのspecsを取得するには "-dumpspecs" オプションを指定します。 $ gcc -dumpspecs > specs.default 参考資料: - GCC 4.1.2 関連オンラインマニュアル -- http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Option-Summary.html -- http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Link-Options.html -- http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Spec-Files.html - binutils "ld" オンラインマニュアル -- http://sourceware.org/binutils/docs-2.21/ld/Options.html - gcc 4: no specs file -- http://www.linuxquestions.org/questions/linux-from-scratch-13/gcc-4-no-specs-file-341034/ - 5.8. ツールチェーンの調整 -- http://lfsbookja.sourceforge.jp/6.7.ja/chapter05/adjusting.html - 猫科研究所(felid labo) - GCCのspecsとは -- http://up-cat.net/GCC%25A4%25CEspecs%25A4%25C8%25A4%25CF.html "-dumpspecs"でダンプしたspecsファイルをカスタマイズし、 "-specs" オプションで指定することにより、アセンブラ(as)やリンカ(ld)に渡すコマンドラインオプションをカスタマイズすることが出来ます。 実際にダンプしてみると分かりますが、デフォルトのspecsはそれなりの分量があります。 それら全てを手直しする必要はありません。変更したい箇所だけを修正し、それ以外は削除してしまって構いません。削除されたものについては、デフォルトのspecsが適用されます。 今回使用するspecsファイルを紹介します。 specs: #pre||> *uclibc_dev_path: /home/msakamoto/reduced.linux/uclibc_test/dev/ *uclibc_crt_path: %(uclibc_dev_path)usr/lib/ *uclibc_runtime_path: /home/msakamoto/reduced.linux/uclibc_test/runtime/ *endfile: %(uclibc_crt_path)crtn.o *startfile: %(uclibc_crt_path)crt1.o %(uclibc_crt_path)crti.o *dynamic_linker: %(uclibc_runtime_path)lib/ld-uClibc.so.0 ||< 静的リンクのケースは無視し、crtX.oとdynamic_linkerをuClibcのものに変更するだけであればこれだけでOKです。 ではこのspecsファイルを使って helloworld.c をコンパイルしてみます。 $ pwd /home/msakamoto/reduced.linux/uclibc_test $ gcc -nostdinc \ -I`pwd`/dev/usr/include \ -I/usr/include \ -isystem /usr/lib/gcc/i386-redhat-linux/4.1.2/include-fixed \ -isystem /usr/lib/gcc/i386-redhat-linux/4.1.2/include \ -c -o hw_dyn.o helloworld.c $ gcc -specs specs -L`pwd`/dev/usr/lib -L`pwd`/runtime/lib -o hw_dyn hw_dyn.o $ readelf -x .interp hw_dyn Hex dump of section '.interp': 0x080480f4 2f6f746f 6d616b61 736d2f65 6d6f682f /home/msakamoto/ 0x08048104 63752f78 756e696c 2e646563 75646572 reduced.linux/uc 0x08048114 6d69746e 75722f74 7365745f 6362696c libc_test/runtim 0x08048124 2e636269 6c43752d 646c2f62 696c2f65 e/lib/ld-uClibc. 0x08048134 00 302e6f73 so.0. $ ./hw_dyn abc def Hello, World! LINUX_VERSION_CODE=132626 args[0]=[./hw_dyn] args[1]=[abc] args[2]=[def] gccが裏で行っている処理を確認したい場合は "-v" オプションを追加してgccを実行してください。 他、メモ書き: - "-I"や"-isystem"についてもspecsに組み込めれば楽なのですが、面倒くさいので諦めました。これくらいならCFLAGSに指定しても許容できる範囲だと判断しました。 - "-nostdlib" を外していますが、これは libgcc.a 系をデフォルトのspecsに従い組み込めるようにしたためです。 -- "-nostdlib" を外すとlibgcc.a系をリンクしなくなります。するとコンパイル時に"-fno-builtin"オプション(libgcc.a系列で提供されているGCC組み込み関数を使わなくするオプション)が必要になりますが、さすがにそこまで強制する気にはなれませんでした。 #navi_footer|技術|