蝉は、やがて死ぬる午後に気づいた。ああ、私たち、もっと仕合せになってよかったのだ。:2010年04月15日分

2010/04/15(Thu)

[NetBSD] Only one ABI change in libc, in more than 15 years!(その2)

昨日の都築。

libfoo12.tar.gzの状態ですと Makefile は

1 LIB=            foo
2 CPPFLAGS+=      -I${.CURDIR}
3 COMPATDIR=      ${.CURDIR}/compat
4 
5 .include "${.CURDIR}/foo/Makefile.inc"
6 .include "${COMPATDIR}/Makefile.inc"
7 
8 .include <bsd.lib.mk>

5~6行目で libfoo12/foo/Makefile.inc と libfoo12/compat/Makefile.inc をインクルードしていますので
libfoo12/foo/foo.c と libfoo12/compat/foo/compat_foo.c がコンパイルされ
共有ライブラリの libfoo.so が作られることになります。

$ cd libfoo12
$ make obj
$ make depend all
$ ls obj*/*.pico
obj.i386/compat_foo.pico  obj.i386/foo.pico

あ、HEADではつい先日-fPICあり(position independent code)でコンパイルされたオブジェクトファイルの
拡張子が *.so から *.pico に変更になってますので、5 系使いの方は注意。

libfoo12/foo/foo.cは↓のようなコードになってます。

 1 #include <sys/cdefs.h>
 2 #include <foo.h>
 3 #include <stdio.h>
 4 
 5 void
 6 foo()
 7 {
 8 #if defined(__LIBFOO12_SOURCE__)
 9         puts("hello, libfoo12.");
10 #else
11         puts("hello, libfoo13.");
12 #endif
13 }

__LIBFOO12_SOURCE__が定義されているか否かで
挨拶文が変わる=挙動が変わりますが、これがABI変更(のつもり)です。
このファイルはそのままコンパイルされますので出力する挨拶文は "hello, libfoo13." です。

またこのファイルは libfoo12/foo/Makefile.inc で

1 .PATH: ${.CURDIR}/foo
2 
3 SRCS+=                  foo.c
4 CPPFLAGS.foo.c+=        -I${.CURDIR}/include

のように、libfoo12/include に対してパスが設定されていますので
include/foo.h つまりは↓な内容をインクルードしとります。

 1 #ifndef _FOO_H_
 2 #define _FOO_H_
 3 
 4 #include <sys/cdefs.h>
 5 
 6 __BEGIN_DECLS
 7 void foo(void)  __RENAME(__foo60);
 8 __END_DECLS
 9 
10 #endif /*_FOO_H_*/

ですので foo シンボルは __RENAME マクロによって __foo60 に置換されとるわけです。

vmware$ nm obj*/foo.pico | grep foo
00000000 T __foo60

一方 libc12/compat/foo/compat_foo.c は以下のようになっています。

1 #define __LIBFOO12_SOURCE__
2 
3 #include <sys/cdefs.h>
4 
5 __warn_references(foo,
6     "warning: reference to compatibility foo();"
7     " include <foo.h> for correct reference")
8 
9 #include "foo/foo.c"

このファイルで注意すべき点は第一に、1行目で__LIBFOO12_SOURCE__が定義されていますので
9行目でインクルードした foo/foo.c は "hello, libfoo12." と表示することになるのと
以前説明した__warn_references()マクロによってリンク時に警告を出しているということです。

このままコンパイルしてしまいますと libc12/foo/foo.c と同じように
__RENAME マクロの魔力で foo が __foo60 に 置換されてしまい

libfoo_pic.a(foo.pico): In function `__foo60':
foo.c:(.text+0x0): multiple definition of `__foo60'
libfoo_pic.a(compat_foo.pico):compat_foo.c:(.text+0x0): first defined here
*** Error code 1

Stop.

と重複シンボルによるエラーが起きてしまうのですが、ここでちょっとしたトリックで
libfoo12/compat/foo/Makefile.inc において

1 .PATH: ${COMPATDIR}/foo
2 SRCS+=                  compat_foo.c
3 CPPFLAGS.compat_foo.c+= -I${COMPATDIR}/include

として libfoo12/include ではなく libfoo12/compat/include にパスが通してあり、こっちでは

1 #ifndef _COMPAT_FOO_H_
2 #define _COMPAT_FOO_H_
3 
4 __BEGIN_DECLS
5 void foo(void);
6 __END_DECLS
7 
8 #endif /*_FOO_H_*/

と シンボル置換が行われず、foo のままになるということです。

$ nm obj*/compat_foo.pico | grep foo
00000000 T foo

簡単にまとめると

ということです、なんかディレクトリが増えたり同じヘッダを2バージョン用意せなあかんかったりで
余計ややこしくなっただけに思えますが、これが実際にこの libfoo の major を crunk する時に
なるほどーという効果を生むようになるのです、以下次回に続く。