2

cpp / hファイルが関連付けられた共有ライブラリ(つまりlibXXX.so)があります。これらには、いくつかの関数ポインタ(.so関数エントリポイントを指す)と、このクラスのメソッドとしてこの関数をラップするクラスが含まれています。

すなわち:.hファイル:

typedef void* handle;
/* wrapper functions */
handle okUsbFrontPanel_Construct();
void okUsbFrontPanel_Destruct(handle hnd);

/* wrapper class */
class okCUsbFrontPanel
{
public:
  handle h;
public:
  okCUsbFrontPanel();
  ~okCUsbFrontPanel();
};

.cppファイル

/* class methods */
okCUsbFrontPanel::okCUsbFrontPanel()
  { h=okUsbFrontPanel_Construct(); }
okCUsbFrontPanel::~okCUsbFrontPanel()
  { okUsbFrontPanel_Destruct(h); }
/* function pointers */
typedef handle  (*OKUSBFRONTPANEL_CONSTRUCT_FN) (void);
typedef void    (*OKUSBFRONTPANEL_DESTRUCT_FN)  (handle);
OKUSBFRONTPANEL_CONSTRUCT_FN    _okUsbFrontPanel_Construct = NULL;
OKUSBFRONTPANEL_DESTRUCT_FN _okUsbFrontPanel_Destruct = NULL;
/* load lib function */
Bool LoadLib(char *libname){
  void *hLib = dlopen(libname, RTLD_NOW);
  if(hLib){
    _okUsbFrontPanel_Construct = ( OKUSBFRONTPANEL_CONSTRUCT_FN ) dlsym(hLib, "okUsbFrontPanel_Construct");
    _okUsbFrontPanel_Destruct = ( OKUSBFRONTPANEL_DESTRUCT_FN ) dlsym( hLib, "okUsbFrontPanel_Destruct" );
  }
}
/* construct function */
handle okUsbFrontPanel_Construct(){
  if (_okUsbFrontPanel_Construct){
    handle h = (*_okUsbFrontPanel_Construct)(); //calls function pointer
    return h;
  }
  return(NULL);
}

void okUsbFrontPanel_Destruct(handle hnd)
{
  if (_okUsbFrontPanel_Destruct)
    (*_okUsbFrontPanel_Destruct)(hnd);
}

次に、次の呼び出しを行う別の共有ライブラリ(自分で作成)があります。

LoadLib("libXXX.so");
okCusbFrontPanel *device = new okCusbFrontPanel();

セグメンテーション違反が発生します。セグメンテーション違反はで発生するようです

handle h = (*_okUsbFrontPanel_Construct)();

しかし、奇妙な振る舞いで:私が到達すると

(*_okUsbFrontPanel_Construct)(); 

okUsbFrontPanel_Construct()への再帰を取得します。

誰かが何か考えを持っていますか?

編集:これは、gdbを使用した実行によって取得されたバックトレースです。

#0  0x007590b0 in _IO_new_do_write () from /lib/tls/libc.so.6
#1  0x00759bb8 in _IO_new_file_overflow () from /lib/tls/libc.so.6
#2  0x0075a83d in _IO_new_file_xsputn () from /lib/tls/libc.so.6
#3  0x00736db7 in vfprintf () from /lib/tls/libc.so.6
#4  0x0073ecd0 in printf () from /lib/tls/libc.so.6
#5  0x02cb68ca in okCUsbFrontPanel (this=0x9d0ae28) at okFrontPanelDLL.cpp:167
#6  0x03cac343 in okUsbFrontPanel_Construct () from /opt/atlas/tdaq/tdaq-02-00-00/installed/i686-slc4-gcc34-dbg/lib/libokFrontPanel.so
#7  0x02cb8f36 in okUsbFrontPanel_Construct () at okFrontPanelDLL.cpp:1107
#8  0x02cb68db in okCUsbFrontPanel (this=0x9d0ade8) at okFrontPanelDLL.cpp:169
#9  0x03cac343 in okUsbFrontPanel_Construct () from /opt/atlas/tdaq/tdaq-02-00-00/installed/i686-slc4-gcc34-dbg/lib/libokFrontPanel.so
#10 0x02cb8f36 in okUsbFrontPanel_Construct () at okFrontPanelDLL.cpp:1107
#11 0x02cb68db in okCUsbFrontPanel (this=0x9d0ada8) at okFrontPanelDLL.cpp:169
#12 0x03cac343 in okUsbFrontPanel_Construct () from /opt/atlas/tdaq/tdaq-02-00-00/installed/i686-slc4-gcc34-dbg/lib/libokFrontPanel.so
#13 0x02cb8f36 in okUsbFrontPanel_Construct () at okFrontPanelDLL.cpp:1107

など...私見私はある種のスタックオーバーフローのためにセグメンテーション違反を起こします。再帰呼び出しが多すぎて、問題が発生します。

ちなみに、私はScientific Linux 4ディストリビューション(RH4ベース)を使用しています。

EDIT2:

関数okUsbFrontPanel_Construct出力のlibokFrontPanel.soのobjdump:

00009316 <okUsbFrontPanel_Construct>:
9316:   55                      push   ebp  
9317:   89 e5                   mov    ebp,esp
9319:   56                      push   esi
931a:   53                      push   ebx
931b:   83 ec 30                sub    esp,0x30
931e:   e8 44 f4 ff ff          call   8767 <__i686.get_pc_thunk.bx>
9323:   81 c3 dd bd 00 00       add    ebx,0xbddd
9329:   c7 04 24 38 00 00 00    mov    DWORD PTR [esp],0x38
9330:   e8 93 ec ff ff          call   7fc8 <_Znwj@plt>
9335:   89 45 e4                mov    DWORD PTR [ebp-28],eax
9338:   8b 45 e4                mov    eax,DWORD PTR [ebp-28]
933b:   89 04 24                mov    DWORD PTR [esp],eax
933e:   e8 65 ed ff ff          call   80a8 <_ZN16okCUsbFrontPanelC1Ev@plt>
9343:   8b 45 e4                mov    eax,DWORD PTR [ebp-28]
9346:   89 45 f4                mov    DWORD PTR [ebp-12],eax
9349:   8b 45 f4                mov    eax,DWORD PTR [ebp-12]
934c:   89 45 e0                mov    DWORD PTR [ebp-32],eax
934f:   eb 1f                   jmp    9370 <okUsbFrontPanel_Construct+0x5a>
9351:   89 45 dc                mov    DWORD PTR [ebp-36],eax
9354:   8b 75 dc                mov    esi,DWORD PTR [ebp-36]
9357:   8b 45 e4                mov    eax,DWORD PTR [ebp-28]
935a:   89 04 24                mov    DWORD PTR [esp],eax
935d:   e8 d6 f2 ff ff          call   8638 <_ZdlPv@plt>
9362:   89 75 dc                mov    DWORD PTR [ebp-36],esi
9365:   8b 45 dc                mov    eax,DWORD PTR [ebp-36]
9368:   89 04 24                mov    DWORD PTR [esp],eax
936b:   e8 a8 f0 ff ff          call   8418 <_Unwind_Resume@plt>
9370:   8b 45 e0                mov    eax,DWORD PTR [ebp-32]
9373:   83 c4 30                add    esp,0x30
9376:   5b                      pop    ebx
9377:   5e                      pop    esi
9378:   5d                      pop    ebp
9379:   c3                      ret    

933eで、実際に<_ZN16okCUsbFrontPanelC1Ev @ plt>への呼び出しがあります。この呼び出しは、私の.cpp内の呼び出しと混同されますか?

4

3 に答える 3

8

出力を投稿GDBしたので、問題が何であるかは明確です。

libokFrontPanel.soと で同じシンボルを定義しているlibLoadLibrary.soため (より適切な名前がないため、適切な名前を付ける説明がはるかに簡単になります)、それが無限再帰の原因となっています。

UNIX のデフォルトでは (Windows とは異なり)、すべての共有ライブラリ (およびメインの実行可能ファイル) からのすべてのグローバル シンボルは、単一の「ローダー シンボル名前空間」に入ります。

とりわけ、これはmalloc、メインの実行可能ファイルで定義すると、 (独自の定義がある場合でも) すべての共有ライブラリによって呼び出されることを意味 します。malloclibclibcmalloc

それで、ここで何が起こっているのですか:libLoadLibrary.soあなたが定義しokCUsbFrontPanelたコンストラクターで。その正確な記号の定義が にもあると断言しlibokFrontPanel.soます。このコンストラクターへのすべての呼び出し (デフォルト) は、最初の定義 (動的ローダーが最初に観察したもの) に行きますが、作成者はlibokFrontPanel.soこれが起こることを意図していませんでした。ループは次のとおりです(同じ順序でGDB印刷されます-最も内側のフレームが上になります):

 #1 okCUsbFrontPanel () at okFrontPanelDLL.cpp:169
 #3 okUsbFrontPanel_Construct () from libokFrontPanel.so
 #2 okUsbFrontPanel_Construct () at okFrontPanelDLL.cpp:1107
 #1 okCUsbFrontPanel () at okFrontPanelDLL.cpp:169

からのコンストラクターへの呼び出しは#3、シンボル #4 に移動することを意図していました -okCUsbFrontPanelコンストラクター libokFrontPanel.so。代わりに、内部の以前に見られた定義に移動しましたlibLoadLibrary.so。シンボル #4 を「プリエンプト」したため、無限再帰ループが形成されました。

道徳: 複数のライブラリで同じシンボルを定義しないでください。実行時ローダーがどのシンボル参照がどの定義にバインドされるかを決定する規則を理解していない限り。

EDIT:質問の「EDIT2」に答えるには:
はい、_ZN16okCUsbFrontPanelC1Evfromへの呼び出しokUsbFrontPanel_Constructは、あなたの 内のそのメソッドの定義に行きますokFrontPanelDLL.cpp。調べてみるとわかりやすいかもしれませんobjdump -d okFrontPanelDLL.o

于 2009-07-31T06:06:47.823 に答える
2

Norman Ramseyの言うこととは反対に、segfaultsを診断するために選択するツールはではGDBありませんvalgrind
後者は、特定の種類のsegfaultにのみ役立ちます(ほとんどの場合、これらはヒープの破損に関連しています。ここではそうではないようです)。

私の水晶玉は、あなたdlopen()が失敗したこと(それが起こった場合は印刷する必要がdlerror()あります!)、そしてあなた_okUsbFrontPanel_Constructは残っていると言っていますNULL。で、そのGDB推測が正しいかどうかをすぐに判断できます。

私の推測は、「okUsbFrontPanel_Construct()への再帰を取得する」というあなたのステートメントと矛盾します。しかし、あなたが一緒に見ていなかった場合、どうやってあなたがそのような再帰を得るのかを知ることができますGDBか?

于 2009-07-26T22:50:03.887 に答える
0

segfaultsを診断するために選択するツールはvalgrindです。ポインタやメモリを誤用している場合、valgrindは問題を検出し、セグメンテーション違反が発生するかなり前にスタックトレースを提供します。FAQで、valgrindは、を呼び出さない限り、共有ライブラリを処理できると主張していますdlclose()

これまでvalgrindを使用したことがない場合は、valgrindがいかに簡単で強力であるかに驚かれると思います。コマンドラインの最初の単語として「valgrind」を使用するだけで、メモリエラーが検出されます。素晴らしいもの!VladislavVyshemirskyのブログに短いサンプルセッションがあります。

于 2009-07-25T14:43:49.073 に答える