まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:gnu

自分は(慣れの要素が大きいのですが)CentOS は未だに 6.x をメインに使っています。「別に不便じゃないし~」程度にしか考えていなかったのですが、その姿勢を改めた方がいいかなあ、と思うことがあったので、その時の様子と対処がいかに大変だったかをまとめました。

Linux で比較的新しめのアプリケーションを使おうとすると、たまにこんなエラーメッセージが出ることがあります:
./xxxxx: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./xxxxx)
./xxxxx: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.14' not found (required by ./xxxxx)
./xxxxx: /usr/lib64/libstdc++.so.6: version `CXXABI_1.3.5' not found (required by ./xxxxx)
./xxxxx: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /opt/Xxxxx/libnode.so)

これはシステムにインストールされている libstdc++(GLIBC) ライブラリが古く、動かそうとしているアプリケーションが要求するバージョン(上の例では 3.4.14 や 3.4.15)と合わないためのエラーが発生していることを意味しています。


※実はこのエラーはこのブログエントリの最後に「Rodeo は CentOS/RHEL 7.x 上で動かすのが無難」と書いた時の話です。CentOS 6.8 で普通に Rodeo をインストールして動かすと、ここで紹介しているようなエラーに遭遇するのでした:
CentOS に Rodeo(Python IDE)をインストールする


実際にシステムに組み込まれている libstdc++ のバージョンを確認するには以下のコマンドを実行します(青字は出力結果):
# strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX

GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH

この例の場合、3.4.1 から 3.4.13 までは組み込まれているシステムであることが分かります。このシステムで上記アプリケーションを動かそうとすると 3.4.14 や 3.4.15 は組み込まれていないため、上述のようなエラーが発生してしまう、ということになるのでした。エラーの原因はこれで特定できました。


この状況を回避するには libstdc++ を必要なバージョンが組み込まれた新しいものと入れ替える必要があります。そして libstdc++ は gcc に含まれるモジュールなので、(まず gcc ビルドの前提となる glibc を更新して、)gcc をビルドして、そこから libstdc++ を取り出して既存のものと入れ替える、というちと面倒な手順を実行する必要があるのでした。要するに libstdc++ を新しくするためには gcc をビルドする必要があるのです。。

更に、ここまでは全ての Linux ディストリビューションに言える内容なのですが、実際にこの手順を行う場合、CentOS/RHEL 6.x だとものすごく面倒な手順が待ち構えているのでした。実際に行った長い道のりを以下に紹介します。



まず、gcc のビルドに必要なヘッダーファイルをインストールします。具体的には gmp-devel, mpfr-devel, libmpc-devel が必要で、かつ 64bit 環境では glibc-devel.i686 までも必要になります。このうち libmpc-devel 以外は標準の yum リポジトリからインストールできますが、libmpc-devel は EPEL から入手する必要があります。

というわけで、まずは EPEL リポジトリをダウンロード:
# yum install epel-release

続けて上記3つのヘッダファイルと 32bit 版 glibc ヘッダをダウンロードします:
# yum install gmp-devel mpfr-devel libmpc-devel
# yum install glibc-devel.i686

ヘッダファイルの準備ができた所で最初に前提となる glibc をダウンロードしてビルドします。以下の例では /usr/local/src 以下にソースコードを展開しています:
# cd /usr/local/src
# wget http://ftp.gnome.org/pub/gnome/sources/glib/2.32/glib-2.32.4.tar.xz
# tar Jxvf glib-2.32.4.tar.xz
# rm glib-2.32.4.tar.xz
# cd glib-2.32.4
# ./configure
# make
# make install

そして、ここからやっと gcc のソースコードをダウンロードしてビルドします。ちなみにここから先の手順を行うためには 5GB 弱の空きディスク容量が必要になります。また make の実行を開始してから完了するまでに数時間かかります。スペックにもよりますが、昼寝ではなく一度マジ寝して起きる頃に終わっている、というくらいの時間を見ておく必要があります (^^;:
# cd /usr/local/src
# curl -LO http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-4.8.4/gcc-4.8.4.tar.gz
# tar fxz gcc-4.8.4.tar.gz
# rm gcc-4.8.4.tar.gz
# cd gcc-4.8.4
# ./configure
# make (僕の仮想環境ではここで4~5時間かかりました・・)
# make install

・・・で、gcc のビルドが無事に完了したら libstdc++ の入れ替えを行います。まずは現在の状況を確認します:
# ls -l /usr/lib64/libstdc*

lrwxrwxrwx 1 root root     19  1月 23 12:35 2017 /usr/lib64/libstdc++.so.6 -> libstdc++.so.6.0.13
-rwxr-xr-x 1 root root 989840  5月 10 18:38 2016 /usr/lib64/libstdc++.so.6.0.13

ビルドした結果から、目的のライブラリをコピーして、シンボリックリンクを作り直します:
# cp /usr/local/src/gcc-4.8.4/x86_64-unknown-linux-gnu/libstdc++-v3/src/.libs/libstdc++.so.6.0.19 /usr/lib64
# cd /usr/lib64
# mv libstdc++.so.6 libstdc++.so.6.bak
# ln -s libstdc++.so.6.0.19 libstdc++.so.6
# ls -l /usr/lib64/libstdc*

lrwxrwxrwx 1 root root      19  2月 14 12:07 2017 /usr/lib64/libstdc++.so.6 -> libstdc++.so.6.0.19
-rwxr-xr-x 1 root root  989840  5月 10 18:38 2016 /usr/lib64/libstdc++.so.6.0.13
-rwxr-xr-x 1 root root 6468627  2月 14 12:06 2017 /usr/lib64/libstdc++.so.6.0.19
lrwxrwxrwx 1 root root      19  5月 26 11:23 2016 /usr/lib64/libstdc++.so.6.bak -> libstdc++.so.6.0.13

最後に目的のバージョン(今回であれば 3.4.14 や 3.4.15)が有効になっているかどうかを確認します:
# strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX

GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_3.4.18
GLIBCXX_3.4.19
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGTH

期待通りのライブラリに更新できました。これで CentOS 6.x でも Rodeo が動くようになりました。いやあ、長かった。めでたし、めでたし:
rodeo_centos6
↑今回のブログエントリは、この「CentOS 6.x 上で動く Rodeo」のスクリーンショットを撮るまでがいかに大変であったかを分かっていただきたいがために書きました。 (^^;


ここまで確認できれば glibc や gcc のソースコード一式は不要なので、消してしまっても構いません(何しろこの環境のために 5GB 前後のディスクを専有しているので・・・)
# cd /usr/local/src
# rf -rf glib-2.32.4 # rm -rf gcc-4.8.4




(参考)
http://qiita.com/dozo/items/de393588d5c267794ced

https://www.saintsouth.net/blog/update-libstdcpp-on-centos6/



 

20数年前の話ですが、情報処理技術者2種試験に合格しました。その時、プログラミング言語は Fortran を選択しました。

が、今では「Fortran の書き方すら忘れた」状態でした。"Fortran" が "Formula translation" の略だった、ということ程度しか記憶していません。

実際には Fortran の前に BASIC や C を学んではいました。ただ少なくとも当時の情報処理技術者試験はプログラミング言語実習でこれらの言語を選択することはできませんでした。苦渋の選択として大学で学んだ Fortran を選んだのでした(ちなみに1種の時は Pascal と CASL です)。

そんな Fortran を20数年ぶりに使ってみました。せっかくなので、単なる "Hello World" だけではなく、(当時はこんな使い方すると思ってなかった)ウェブの CGI 言語として Fortran が使えないか挑戦してみました。環境はいつもの CentOS 6 です。


まずはコンパイラのインストールです(よく考えたら Fortran のインストール自体初めてかも・・)。CentOS の Fortran としてはいくつか選択肢がありますが、個人的な好みの理由で "g77" という GNU のコンパイラを使うことにします。インストールは yum でサクッと:
# yum install gcc* compat-gcc*

これで g77 という Fortran コンパイラが導入されているはずです。"--help" オプションを付けて実行するとオンラインヘルプが表示されます:
# g77 --help
使い方: g77 [オプション] ファイル...
オプション:
  -pass-exit-codes         フェーズからのエラーコードの最大値を exit
                               コードとして返す
  --help                   このヘルプ情報を表示
  --target-help            ターゲット固有のコマンドラインオプションを表示
   :
   :

まずは "Hello World" に(再)挑戦します。ソースはこんな感じの hello.f を用意しました。各行の最初の6文字はラベル用にリザーブされているので使いません(各行の最初の6文字は半角スペースです):
      program hello
      print *, 'Hello World!'
      end program hello

これをコンパイルします(コンパイル結果を hello という名前で出力するように指定しています):
# g77 hello.f -o hello

で、実行するとこんな感じ(青字が出力結果)。期待通りに動いてます:
# ./hello
 Hello World!
#


ここまでは簡単、問題はここから。結論を先に言うと、Fortran で CGI を実現してみようとすると結構ハマります(苦笑)。CGI の実現のため、HTTPD サーバーとして Apache HTTP サーバーをあらかじめ導入しておきます:
# yum install httpd
# /etc/init.d/httpd start

CGI を動かしてみるべく、まずはこんな感じの入力フォームを用意してみました。デフォルト状態であれば /var/www/html/ 以下に(例えば)test1.html という名前で作成します:
<html>
<form method="post" action="./cgi-bin/test1.cgi">
<input name="val1" type="text" maxlength="20"/>
<input name="val2" type="text" maxlength="20"/>
<input type="submit" value="SUBMIT"/>
</form>
</html>

このページを普通にブラウザから開くとこんな感じになります:
2015120501


そして、このページから入力パラメータを受け取って処理する CGI プログラムを用意します。CGI プログラムは /var/www/cgi-bin/ で動くので、このディレクトリに test1.f という名前の以下の様な Fortran プログラムを用意します。内容は文字列変数 a に外部からポストされるデータを格納して、その格納内容を表示する、というものです:
      character*255 a
      write(*,100)
100   format('Content-type: text/html'//)
      read(*,*) a
      write(*,*) '<html>'
      write(*,*) 'test1.cgi が受け取ったパラメータ:<br/>'
      write(*,'(a72)') a
      write(*,*) '</html>'
      stop
      end

このプログラムをコンパイルして、test1.cgi という名前でビルドしておきます(ビルド結果は /var/www/cgi-bin/test1.cgi にある状態):
# g77 test1.f -o test1.cgi


では実際にパラメータを入力して使ってみます。先程の画面に2つのパラメータを入力して、SUBMIT ボタンをクリックしてみます:
2015120502


結果はこのようになります:
2015120503


つまり、2つのフィールドに与えた内容を受け取って val1=(最初の値)&val2=(2番目の値) という一般的な CGI のフォーマットでデータが POST され、それを Fortran のプログラム内の変数 a が受け取れている、ということになります。なんとか Fortran で CGI が実現できました!

でもまだまだ充分とは言えません。例えばパラメータで日本語を入力して SUBMIT した場合・・・
2015120504


(他の言語でも同様ですが)変数 a には当然エンコードされた結果が入ります:
2015120505


ということはこの内容をデコードしないと日本語文字列に戻らないわけです。ただ他の言語と異なり、ウェブで使われることは想定されていないであろう Fortran には、この文字デコードですら結構大変です。

更に、連想配列など持たない Fortran では、このパラメータの解析だけでも結構大変だったりします。"&" で split して、更に split した結果の配列を "=" で split して・・・って、split 関数があるわけではないので、そういう所から自作する必要がありそうです。うわあ、面倒くさそう・・・



というわけで、実用にはまだほど遠い環境ではありますが、とりあえず CGI で Fortran を使う、という所までは実現できました。Fortran でコンパイルした結果だけがあればいいので、理論上は Bluemix でも(Static HTML の Buildpack を使えば)動かせるんじゃないのかな? まあそれはいずれやってみます。

このページのトップヘ