history

青木日記 RSS

<前の日 | この月 | 次の日>

2005-02-17

AIX + gcc 3.3.2 + ruby

なぜか AIX で ruby のコンパイルを始めてみた。手強い。

[環境]

  • AIX 5.1L
  • gcc 3.3.2
  • ruby CVS HEAD

[参考資料]

まず何も考えずに configure; make してみると、 miniruby のリンクで止まる。-brtl がよくないらしい。 これはリンカ (/usr/ccs/bin/ld) のオプションなので、 gcc を使う場合は -Wl,-brtl にする必要がある。

それはともあれ、とりあえず先に進めるために -brtl を取ってみると miniruby は作れて動くようになる。

~/obj/ruby $ make miniruby
         gcc main.o libruby-static.a -ldl -lcrypt -lm   -o miniruby -g -O2
~/obj/ruby $ ./miniruby -v -e 'puts "OK"'
ruby 1.9.0 (2005-02-14) [rs6000-aix5.1.0.0]
OK

……が、拡張ライブラリがコンパイルできない。

compiling bigdecimal
        gcc -g -O2  -I. -I../.. -I../../../../src/ruby -I../../../../src/ruby/ext/bigdecimal   -c ../../../../src/ruby/ext/bigdecimal/bigdecimal.c
In file included from ../../../../src/ruby/ruby.h:21,
                 from ../../../../src/ruby/ext/bigdecimal/bigdecimal.c:23:
../../config.h:7:1: warning: "_ALL_SOURCE" redefined
In file included from /opt/freeware/lib/gcc-lib/powerpc-ibm-aix5.1.0.0/3.3.2/include/ctype.h:32,
                 from ../../../../src/ruby/ext/bigdecimal/bigdecimal.c:16:
/usr/include/standards.h:99:1: warning: this is the location of the previous definition
        /usr/ccs/bin/ld -brtl -eInit_bigdecimal -bI:../../ruby.imp -bM:SRE -T512 -H512   -L"../.." -o ../../.ext/rs6000-aix5.1.0.0/bigdecimal.so bigdecimal.o  -ldl -lcrypt -lm   -lc
ld: 0706-003 インポート・ファイルが見つからないか、読み取れません: ../../ruby.imp
        ld:accessx(): このパス名のファイルまたはディレクトリは存在しません。
make: 1254-004 最後のコマンドからのエラー・コードは 255 です。
 
 
停止します。
make: 1254-004 最後のコマンドからのエラー・コードは 1 です。
 
 
停止します。

いろいろ調べた結果、make ruby.imp でエクスポートライブラリを作っておく必要があるとわかった。 めんどくさい。

※ さらに後の調べによって、--enable-shared のときは自動的に作成されることが判明

ruby.imp を作っておくとビルドは問題なく最後まで行くようになったが、 今度は拡張ライブラリがロードできない。

~/obj/ruby $ sudo make install
略
~/obj/ruby $ /usr/local/pkg/ruby/bin/ruby --version
ruby 1.9.0 (2005-02-14) [rs6000-aix5.1.0.0]
~/obj/ruby $ /usr/local/pkg/ruby/bin/ruby -rbigdecimal -e0
./bigdecimal.so: load failed - ./bigdecimal.so (LoadError)

さてどーするかな。

まず、小さいアプリケーションでもって、 dlopen を使ったライブラリルーチンの正しい呼びかたを確認してみる。 構成はできるだけ ruby に似せて次のようにする。

  • main() の定義を持つ main.c。dlopen() で extfunc() を呼ぶ。
  • extfunc() の定義を持つ extlib.so (extlib.c)。rb_int_twice() を呼ぶ。
  • rb_int_twice() の定義を持つ libruby.a (libruby.c)。

このとき次のようにすると動作するライブラリが作れる。

~/c/test/c/shlib $ make
        gcc -c -Wall main.c -o main.o
        gcc -c -Wall libruby.c -o libruby.o
        gcc -shared -Wl,-G libruby.o -o libruby.so
        gcc -c -Wall extlib.c -o extlib.o
        gcc -shared -Wl,-G -Wl,-rtllib extlib.o -L. -lruby -o extlib.so
ld: 0711-224 警告: シンボル ._GLOBAL__DI が重複しています。
ld: 0711-224 警告: シンボル ._GLOBAL__DD が重複しています。
ld: 0711-224 警告: シンボル _GLOBAL__DI が重複しています。
ld: 0711-224 警告: シンボル _GLOBAL__DD が重複しています。
ld: 0711-345 -bloadmap または -bnoquiet オプションを使用して、詳細な情報を得てください。
        gcc main.o -Wl,-brtl -L. -lruby -o ruby
ターゲット "all" は最新のものです。
~/c/test/c/shlib $ ./ruby
result=25

_GLOBAL__DI, _GLOBAL__DD は C++ のコンストラクタとデストラクタらしい。 無視しておこう。

ポイントは 4 点ある。

  • ライブラリを作るときは gcc -shared -Wl,-G
  • 拡張ライブラリを作るときは -Wl,-brtllib も必要
  • 拡張ライブラリには libruby をリンクしておかないといけない。 ひらたく言うと --enable-shared が必要。
  • 実行可能ファイルを作るときには -Wl,-brtl が必要。

拡張ライブラリに libruby をリンクするのは、 rb_xxx_xxxx() をリンクするため。 どうも AIX では単純に dlopen するだけだと 拡張ライブラリ → ruby のシンボル解決ができないっぽい。 だから libruby を独立させて、 コンパイル時にシンボルを解決してしまう必要がある。

また現在の ruby は拡張ライブラリを作るときに ld -eInit_xxxx -bM:SRE -T512 -H512 というフラグを使っているが、 これは必要ないようだ。-G -brtllib だけ付けておけばよい。 man ld には次のようにある。

-G Produces a shared object enabled for use with the run-time linker. The -G
flag is equivalent to specifying the erok, rtl, nortllib, nosymbolic,
noautoexp, and M:SRE options with the -b flag. Subsequent options can
override these options. This flag only applies to AIX 4.2 or later.

共有ライブラリを作るときに必要なのは -bM:SRE オプションなんだが、 これは -G では暗黙のうちに指定される。 また -berok により未定義シンボルが許可されるので、 明示的にシンボルをインポートする必要はない (つまり ruby.imp とかのあたりがすべて必要なくなる)。

念のため他のフラグも見ておく。

-brtl はオブジェクトファイルにライブラリの名前を埋め込むオプションで、 共有ライブラリにリンクするオブジェクトファイルをビルドするときに必要。 また -l オプションのライブラリを探すとき、*.a 以外に *.so も見るようになる。 -G を付けると暗黙のうちに -brtl もセットされる。

ちなみに AIX では共有ライブラリも *.a という名前を持つが、 中身は *.so と一緒 (ECOFF オブジェクトファイル) である。 さらに言えば共有ライブラリはバージョニングされない。 このへんは Tru64UNIX と似ているようだ。

-brtllib は librtl をリンクするフラグで、 dlopen でロードされるライブラリに必要。 -G は暗黙のうちに -bnortllib をセットするので、 -brtllib を明示的に追加する必要がある。

以上の知見をもとに Makefile と config.status を 書き換えまくってみたところ、ビルドまではうまくいった。 しかし拡張ライブラリをロードすると失敗する。

あれ?

あれれ?

……dlopen が使われてないじゃねえかー! これが原因か! dln.c を書き換えて #ifdef AIX を抹殺する。

~/obj/ruby-shared $ ruby -v -rbigdecimal -e 'p $"'
ruby 1.9.0 (2005-02-14) [rs6000-aix5.1.0.0]
["bigdecimal.so"]

勝利!

ん? そういえば rpath を設定してないのになんで実行できるんだろ。 もしかしてカレントディレクトリに libruby.so があるからか。 移動するとどうなる?

~/obj/ruby-shared $ cd
~ $ ruby --version
exec(): 0509-036 以下のエラーのためにプログラム ruby をロードできません:
        0509-150   従属モジュール libruby.so をロードできませんでした。
        0509-022 モジュール libruby.so をロードできません。
        0509-026 システム・エラー: このパス名のファイルまたはディレクトリは存在しません。

だめじゃん……。

どーすればいいんだろう。 ld -blibpath:$prefix/lib:/usr/lib かな?

~/obj/ruby-shared $ gcc -Wl,-brtl -Wl,-blibpath:/usr/local/pkg/ruby/lib:/usr/lib main.o -L. -lruby -ldl -lcrypt -lm -lc -o ruby
~/obj/ruby-shared $ sudo cp ruby /usr/local/pkg/ruby/bin/
~/obj/ruby-shared $ cd
~ $ ruby --version
ruby 1.9.0 (2005-02-14) [rs6000-aix5.1.0.0]

うまくいったもよう。確認。

~ $ dump -H /usr/local/pkg/ruby/bin/ruby
 
/usr/local/pkg/ruby/bin/ruby:
 
                        ***Loader Section***
                      Loader Header Information
VERSION#         #SYMtableENT     #RELOCent        LENidSTR
0x00000001       0x00000015       0x00000071       0x0000004e
 
#IMPfilID        OFFidSTR         LENstrTBL        OFFstrTBL
0x00000004       0x00000764       0x0000005d       0x000007b2
 
 
                        ***Import File Strings***
INDEX  PATH                          BASE                MEMBER
0      /usr/local/pkg/ruby/lib:/usr/lib
1                                    libruby.so
2                                    libc.a              shr.o
3                                    librtl.a            shr.o

ちゃんとセットされているようだ。

さて、パッチをどうするかな。 よーするに、パラメータをこうすりゃいいはずなんだが。

LDSHARED   = gcc -shared
LDFLAGS    = -Wl,-brtl -Wl,-blibpath:$(prefix)/lib:/usr/lib -L. -lruby
DLDFLAGS   = -Wl,-G -Wl,-brtllib $(EXTLDFLAGS)
XLDFLAGS   = $(EXTLDFLAGS)
EXTLDFLAGS =
LIBRUBYARG = -L$(topdir) -lruby

あと dln.c の AIX 独自コードを捨てて dlopen にする。 これで全部通るはずだ。

問題は古いバージョンの AIX だなあ。 man を見ると、4.2 より前とそれ以降でかなり劇的に変わっているようだ。 4.1 以前は無視、てことならば、 上記のように変更してしまえばすべてカタが付く。 無視しないのなら、これまでのパラメータを残しつつ変更する必要がある。

(07:05)

AIX + gcc 3.3.2 + ruby (2)

そーいえば socket がコンパイルされてなかった。 mkmf.log を見る限り、IPv6 がらみの getaddrinfo がだめみたいだ。 てっとりばやく --enable-wide-getaddrinfo で通す。

~/obj/ruby-shared/ext/socket $ ruby ../../../../src/ruby/ext/socket/extconf.rb --enable-wide-getaddrinfo
略
~/obj/ruby-shared/ext/socket $ make
略
~/obj/ruby-shared/ext/socket $ ruby -rsocket -e '
s = TCPSocket.open("harmony", 80)
s.puts "GET / HTTP/1.0"
s.puts
puts s.read
s.close
'
HTTP/1.1 200 OK
Date: Wed, 16 Feb 2005 22:13:40 GMT
Server: Apache/1.3.28 (Unix) mod_fastcgi/2.4.2 mod_ruby/1.0.7 Ruby/1.9.0 mod_ssl/2.8.15 OpenSSL/0.9.7b
Last-Modified: Fri, 05 Mar 2004 15:40:57 GMT
ETag: "22529-ff-40489f89"
Accept-Ranges: bytes
Content-Length: 255
Connection: close
Content-Type: text/html
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<title>harmony</title>
</head>
<body>
<h1>harmony</h1>
<p>Apache working on harmony</p>
</body>
</html>

解決。

(07:17)

AIX / エクスポートファイル

AIX 用のエクスポートファイルを作る方法。

ruby は nm を使っているが、 dump を使ってもよいらしい。

# mkexp.rb
 
table = {}
`dump -g #{ARGV[0]}`.each do |line|
  next unless /\A\s*\d/ =~ line
  idx, name = line.split
  table[name.sub(/\A\./, '')] = true
end
puts table.keys.sort

(07:22)

名前
メールアドレス

<前の日 | この月 | 次の日>
2002|04|05|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|04|05|06|09|10|
2009|07|
2010|09|

Copyright (c) 2002-2007 青木峰郎 / Minero Aoki. All rights reserved. LIRS