home ホーム search 検索 -  login ログイン  | reload edit datainfo version cmd icon diff delete  | help ヘルプ

C言語系/NetBSD1.6における setjmp(), longjmp() の実装(2)

C言語系/NetBSD1.6における setjmp(), longjmp() の実装(2)

C言語系 / NetBSD1.6における setjmp(), longjmp() の実装(2)
id: 536 所有者: msakamoto-sf    作成日: 2010-01-04 14:31:05
カテゴリ: Assembler BSD C言語 

前回、NetBSD1.6でのsetjmp/longjmpの中身を追いかけたところ、リンク後には"__setjmp14"/"__longjmp14"というシンボルにいつの間にか名前が変わっていた。
これについて簡単に調べてみた。
結論から言うと、setjmp.h の中で setjmp/longjmpが組み込みアセンブラでの"__setjmp14"/"__longjmp14"の呼び出しにdefineされていた。

まずヘッダーファイルは次のファイルにあった。

/usr/include/setjmp.h

大雑把な構成だけここに載せる。

#ifndef _JB_ATTRIBUTES
#define _JB_ATTRIBUTES  /**/
#else
#endif
 
(...)
 
typedef long jmp_buf[_JBLEN] _JB_ATTRIBUTES;
 
#include <sys/cdefs.h>
 
__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"に定義されている。

#if defined(__cplusplus)
#define __BEGIN_DECLS           extern "C" {
#define __END_DECLS             };
#define __static_cast(x,y)      static_cast<x>(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 <sys/cdefs.h>

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のマクロ展開も把握出来るようになる。

$ 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__"を使ってシンボル名を置換している為だった。



プレーンテキスト形式でダウンロード
現在のバージョン : 1
更新者: msakamoto-sf
更新日: 2010-01-04 14:31:53
md5:6898808f879f789faf2f55ab36429dd8
sha1:aea9b287ba469706c49b0d1fabdff2b437783fd9
コメント
コメントを投稿するにはログインして下さい。