#navi_header|C言語系| 前回、NetBSD1.6でのsetjmp/longjmpの中身を追いかけたところ、リンク後には"__setjmp14"/"__longjmp14"というシンボルにいつの間にか名前が変わっていた。 これについて簡単に調べてみた。 結論から言うと、setjmp.h の中で setjmp/longjmpが組み込みアセンブラでの"__setjmp14"/"__longjmp14"の呼び出しにdefineされていた。 #more|| まずヘッダーファイルは次のファイルにあった。 /usr/include/setjmp.h 大雑把な構成だけここに載せる。 #code|c|> #ifndef _JB_ATTRIBUTES #define _JB_ATTRIBUTES /**/ #else #endif (...) typedef long jmp_buf[_JBLEN] _JB_ATTRIBUTES; #include __BEGIN_DECLS #ifdef __LIBC12_SOURCE__ int __setjmp14 __P((jmp_buf)); void __longjmp14 __P((jmp_buf, int)); int __sigsetjmp14 __P((sigjmp_buf, int)); void __siglongjmp14 __P((sigjmp_buf, int)); #else /* !__LIBC12_SOURCE__ */ int setjmp __P((jmp_buf)) __RENAME(__setjmp14); void longjmp __P((jmp_buf, int)) __RENAME(__longjmp14); #ifndef _ANSI_SOURCE int sigsetjmp __P((sigjmp_buf, int)) __RENAME(__sigsetjmp14); void siglongjmp __P((sigjmp_buf, int)) __RENAME(__siglongjmp14); #endif /* not ANSI */ #endif /* __LIBC12_SOURCE__ */ #if !defined(_ANSI_SOURCE) && !defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) int _setjmp __P((jmp_buf)); void _longjmp __P((jmp_buf, int)); #endif #if !defined(_ANSI_SOURCE) && !defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE) void longjmperror __P((void)); #endif __END_DECLS ||< いくつか分からない点が出てきた。 - "__BEGIN_DECLS", "__END_DECLS"とは何か? - "__P"とか"__RENAME"とかあるが、これは何か? まず"__BEGIN_DECLS", "__END_DECLS"だが、"sys/cdefs.h"に定義されている。 #code|c|> #if defined(__cplusplus) #define __BEGIN_DECLS extern "C" { #define __END_DECLS }; #define __static_cast(x,y) static_cast(y) #else #define __BEGIN_DECLS #define __END_DECLS #define __static_cast(x,y) (x)y #endif ||< CPPからCの関数を呼ぶ時の常套句をわざわざ "__BEGIN_DECLS", "__END_DECLS"に定義している。 続いて"__P"と"__RENAME"というのが出てきた。 まず"__P"についてだが、これは古いCとANSI Cの宣言形式の違いを吸収するマクロ。"sys/cdefs.h"中に定義されている。 #if __STDC__ || defined(__cplusplus) #define __P(protos) protos /* full-blown ANSI C */ (...) #else /* !(__STDC__ || __cplusplus) */ #define __P(protos) () /* traditional C preprocessor */ (...) 簡単なソースで実験してみる:foo.h: #include int foobar __P((int a, int b)); ANSI-C(デフォルト)としてプリプロセスしてみる: $ cc -E foo.h # 1 "foo.h" # 1 "/usr/include/sys/cdefs.h" 1 3 (...) int foobar (int a, int b) ; このようにANSI Cスタイルの関数宣言となる。 続いて古いC(traditional C)としてプリプロセスしてみる: $ cc -traditional -E foo.h # 1 "foo.h" # 1 "/usr/include/sys/cdefs.h" 1 3 (...) int foobar (); このように、引数が省略された、古いCスタイルの関数宣言となる。 次に"__RENAME"というマクロは、NetBSDにおいて異なるバージョンの共有ライブラリの互換性を維持する為に使われている。 http://www.haskell.org/pipermail/glasgow-haskell-bugs/2008-May/013454.html "__RENAME"マクロは "sys/cdefs.h" にて定義されているが、最終的には"sys/cdefs_elf.h" 中の"___RENAME"マクロになり、"__asm__(シンボル名)"に置換される。 ここまで押さえれば、setjmp.hのマクロ展開も把握出来るようになる。 #pre||> $ cc -E /usr/include/setjmp.h # 1 "/usr/include/setjmp.h" (...) typedef long sigjmp_buf[13 + 1] ; typedef long jmp_buf[13 ] ; int setjmp (jmp_buf) __asm__("__setjmp14" ) ; void longjmp (jmp_buf, int) __asm__("__longjmp14" ) ; int sigsetjmp (sigjmp_buf, int) __asm__("__sigsetjmp14" ) ; void siglongjmp (sigjmp_buf, int) __asm__("__siglongjmp14" ) ; int _setjmp (jmp_buf) ; void _longjmp (jmp_buf, int) ; void longjmperror (void) ; ||< まず int setjmp (jmp_buf) __asm__("__setjmp14" ) ; void longjmp (jmp_buf, int) __asm__("__longjmp14" ) ; int sigsetjmp (sigjmp_buf, int) __asm__("__sigsetjmp14" ) ; void siglongjmp (sigjmp_buf, int) __asm__("__siglongjmp14" ) ; この部分は、setjmp.hの以下の部分が展開されたものだと分かる。 #else /* !__LIBC12_SOURCE__ */ int setjmp __P((jmp_buf)) __RENAME(__setjmp14); void longjmp __P((jmp_buf, int)) __RENAME(__longjmp14); #ifndef _ANSI_SOURCE int sigsetjmp __P((sigjmp_buf, int)) __RENAME(__sigsetjmp14); void siglongjmp __P((sigjmp_buf, int)) __RENAME(__siglongjmp14); #endif /* not ANSI */ #endif /* __LIBC12_SOURCE__ */ 次に int _setjmp (jmp_buf) ; void _longjmp (jmp_buf, int) ; この部分は以下の部分が展開されたもの。 #if !defined(_ANSI_SOURCE) && !defined(_POSIX_C_SOURCE) || defined(_XOPEN_SOURCE) int _setjmp __P((jmp_buf)); void _longjmp __P((jmp_buf, int)); #endif "_setjmp", "_longjmp"についてはX/Open(OpenGroup)にて定義されている。 http://www.opengroup.org/onlinepubs/9699919799/functions/_longjmp.html また"sigsetjmp", "siglongjmp"についてはANSI Cには未定義なので、"#ifndef"で囲っているようだ。 以上より、なぜNetBSD1.6で "setjmp", "longjmp"が"__setjmp14", "__longjmp14"に置換されるのかが判明した。それはNetBSDが共有ライブラリの互換性を維持する為、setjmp.h 中で"__asm__"を使ってシンボル名を置換している為だった。 #navi_footer|C言語系|