Olimex CS-E9302日記 - uClibc を使って hello をコンパイルする。

前回 uClibc を開発ツールチェーンにインストールしたので、これを使って hello をコンパイルする。

$ vi hello.c
#include <stdio.h>
int main(){
  printf("hello\n");
  return 1;
}
$ arm-linux-gcc -o hello hello.c
homerun@homerun-laptop:~/arm-dev$ arm-linux-gcc -o hello hello.c
/usr/local/arm/3.4.1/lib/gcc/arm-linux/3.4.1/../../../../arm-linux/bin/ld: warning: libc.so.6, 
needed by /usr/local/arm/3.4.1/lib/gcc/arm-linux/3.4.1/../../../../arm-linux/lib/libgcc_s.so, 
may conflict with libc.so.0

なにか警告が出ている。 リンカのデフォルトライブラリがおかしいらしい。 回避方法は2つ

(回避1) libgcc_s.soのリンク先を変える

$ su
# cd /usr/local/arm/3.4.1/arm-linux/lib
# rm libgcc_s.so
# ln -s libuClibc-0.9.28.so libgcc_s.so

これでもうまくいくことは確認した。

(回避2) specs というファイルでデフォルトライブラリを変更する。
specs ファイルの場所の確認。

$ arm-linux-gcc -v
Reading specs from /usr/local/arm/3.4.1/lib/gcc/arm-linux/3.4.1/specs
Configured with: 云々... 

次のように変更する。

$ su
# vi /usr/local/arm/3.4.1/lib/gcc/arm-linux/3.4.1/specs
...
*libgcc:
%{msoft-float:-lfloat} %{static|static-libgcc:-lgcc -lgcc_eh} ← この3行は本来1行です
%{!static:%{!static-libgcc:%{!shared-libgcc:-lgcc} ← この3行は本来1行です
%{shared-libgcc:%{!shared: -lgcc}}}} ← この3行は本来1行です
...

表示のため改行しているが、上の3行は1行である。元々あった -lgcc_s の指定を全部消した。

再度 hello をコンパイル

$ arm-linux-gcc -o hello hello.c
$

今度はOK。中身を確認する

$ arm-linux-readelf -a hello
ELF Header:
...
      [Requesting program interpreter: /lib/ld-linux.so.2] ← これは不味い
...
 0x00000001 (NEEDED)                     Shared library: [libc.so.0] ← uClibcである
...
     8: 000083a4   496 FUNC    GLOBAL DEFAULT  UND __uClibc_main

共有ライブラリはuClibc が指定されている。これはOK。

しかし、ローダの指定が/lib/ld-linux.so.2 になっている。これは /lib/ld-uClibc.so.0 になってて欲しい。

回避策は2つ

(回避1) ルートファイルシステムで ld-linux.so.2 のリンク先をld-uClibc-0.9.28.soにする。

# cd ...../lib ← ボード用のルートファイルシステムの下の lib に移動する
# ln -s ld-uClibc-0.9.28.so ld-linux.so.2 ← ローダの調整

これでもうまくいくことを確認した。

(回避2) 若しくは specs ファイルを変更する。

$ su
# vi /usr/local/arm/3.4.1/lib/gcc/arm-linux/3.4.1/specs
*link:
%{!static:--eh-frame-hdr} %{h*} %{version:-v}    ← 元々1行です
%{b} %{Wl,*:%*}    %{static:-Bstatic}    %{shared:-shared}    ← 元々1行です
%{symbolic:-Bsymbolic}    %{rdynamic:-export-dynamic}    ← 元々1行です
%{!dynamic-linker:-dynamic-linker /lib/ld-uClibc.so.0}← 元々1行です
    -X    %{mbig-endian:-EB} -m armelf_linux -p← 元々1行です

表示のため改行しているが、上の5行は元々1行のもの。ローダを ld-uClibc.so.0 に変えた。

(メモ:リンカオプション --dynamic-linker も使える?リンカのman)

再々度 hello をコンパイル

$ arm-linux-gcc -o hello hello.c

中身を確認する

$ arm-linux-readelf -a hello
ELF Header:
...
      [Requesting program interpreter: /lib/ld-uClibc.so.0]
...

ローダの指定もOK。

helloコマンドをルートファイルシステムにコピーする。やり方は前回と同じ。
上の(回避1)を採用した場合はローダのリンクも行う。

なお、ルートファイルシステムの libuClibc-0.9.28.so や ld-uClibc-0.9.28.so はプレインストールのままにしておく。本当はリコンパイルした uClibc で入れ替えるべきだが、既存のBusyBoxが動かなくなる恐れが大なので止めておく。このテストではhelloがちらっと動いてくれればOKなので、、、

出来た ramdisk.gz をボードへ転送する。方法は前回と全く同じ。
転送して、再起動して、ログインする。

~ # ls -al /bin/hello
-rwxr-xr-x   1 root root  4730 Jul 15 2008 /bin/hello
~ # hello
hello

ということで、uClibc にリンクした hello も動く。

ユーザプログラムは、この uClibc 環境でコンパイルすることにする。
カーネル、ドライバについては、uClibc なしのノーマルgcc 環境を別途用意し、そこでコンパイルする予定。
c++, java は使わない方向で。(もし使うなら、 glibc を入れて、uClibc, BusyBox は放棄する)

参考 specs の説明書
specs の説明が少し出てくる