2011-06-23 13 views
11

AndroidでダイナミックローディングAPI(<dlfcn.h>dlopen()dlclose()など)を使用する際にいくつか問題があります。 NDKスタンドアロンツールチェーン(バージョン8)を使用して、アプリケーションとライブラリをコンパイルしています。 Androidのバージョンは2.2.1 Froyoです。アンドロイドプラットフォームでdlclose(...)を使用した場合のセグメント化エラー

シンプルな共有ライブラリのソースコードです。

#include <stdio.h> 

int iii = 0; 
int *ptr = NULL; 

__attribute__((constructor)) 
static void init() 
{ 
    iii = 653; 
} 

__attribute__((destructor)) 
static void cleanup() 
{ 
} 

int aaa(int i) 
{ 
    printf("aaa %d\n", iii); 
} 

上記のライブラリを使用するプログラムソースコードを示します。

#include <dlfcn.h> 
#include <stdlib.h> 
#include <stdio.h> 

int main() 
{ 
    void *handle; 
    typedef int (*func)(int); 
    func bbb; 

    printf("start...\n"); 

    handle = dlopen("/data/testt/test.so", RTLD_LAZY); 
    if (!handle) 
    { 
     return 0; 
    } 

    bbb = (func)dlsym(handle, "aaa"); 
    if (bbb == NULL) 
    { 
     return 0; 
    } 

    bbb(1); 

    dlclose(handle); 
    printf("exit...\n"); 

    return 0; 
} 
すべてが正常に動作しているこれらのソースで

が、私はmain()関数が終了、例えばこれを使用してセグメンテーションフォールト、といくつかのSTL関数やクラス、プログラムがクラッシュを使用しようとすると、共有ライブラリのソースコード。このコードでは

#include <iostream> 

using namespace std; 

int iii = 0; 
int *ptr = NULL; 

__attribute__((constructor)) 
static void init() 
{ 
    iii = 653; 
} 

__attribute__((destructor)) 
static void cleanup() 
{ 
} 

int aaa(int i) 
{ 
    cout << iii << endl; 
} 

、プログラムは、後にセグメンテーションフォールトでクラッシュしたりmain()関数exit中。 私はいくつかのテストを試した結果、次の結果が見つかりました。

  1. STLを使用しないとすべて正常に動作しています。
  2. STLを使用していて、最後にdlclose()を呼び出さないと、すべて正常に動作しています。
  3. -fno-use-cxa-atexit-fuse-cxa-atexitのようなさまざまなコンパイルフラグでコンパイルしようとしましたが、結果は同じです。

STLを使用する私のコードで何が間違っていますか?

あなたは(AAA機能宣言するのextern「C」を使用する必要があります
+0

+1いいえフォーマット済み質問;) –

+0

soヘッダーのSTLヘッダーはファイルですか?あなたはそれをcppファイルに取ってもらえますか? (したがって、STLはインターフェイスにはありません)。定義と宣言は分かれていますか? – Naszta

+0

私はあなたがaaa(...)関数について話していると思います。もしそうなら、宣言と定義は異なるファイルにあります。定義ヘッダーファイルは '#ifdef __cplusplus extern" C " #endif int aaa(int i);' –

答えて

-2

+1

これは適切なヘッダーファイルで行われますが、そのファイルの内容を質問に追加しませんでした。 –

-1

あなたはdlopen()dlclose()を使用しているアプリケーションのためのコンパイラフラグとして-fpicでコンパイルする必要があります。 dlerror()でエラー処理を試して、関数ポインタの割り当てが有効であるかどうかを確認する必要があります。NULLでない場合でも、関数ポインタは初期化から無効なものを指している可能性があります。dlsym()は、シンボルを見つけることができません。 posix準拠のものとは反対のアンドロイドのドキュメントを参照してください。すべてがposixに準拠しているわけではありません。

0

私はdlclose()を呼び出すという一般的な嫌がらせをしています。問題は、マッピングされていない共有ライブラリのコードを実行しようとするものが何もないことを確認しなければならないこと、またはセグメント化エラーが発生することです。

最も一般的な方法は、デストラクタが定義されているオブジェクトを作成するか、共有ライブラリで定義されたコードを呼び出します。オブジェクトがまだdlclose()の後に存在する場合は、オブジェクトが削除されるとアプリがクラッシュします。

logcatを見ると、debuggerdスタックトレースが表示されます。 arm-eabi-addr2lineツールでデコードすることができれば、それがデストラクタにあるかどうかを判断できるはずです。代わりに、クラッシュアドレスを取り出し、上位12ビットを取り除き、それをdlclose()のライブラリへのオフセットとして使用し、そのアドレスにどのようなコードが存在するかを調べようとします。

7

私はバグの理由を見つけたようです。私は、次のソースファイルを別の例を試してみました:ここ は、単純なクラスのソースコードです: myclass.h

class MyClass 
{ 
public: 
    MyClass(); 
    ~MyClass(); 
    void Set(); 
    void Show(); 
private: 
    int *pArray; 
}; 

myclass.cpp

#include <stdio.h> 
#include <stdlib.h> 
#include "myclass.h" 

MyClass::MyClass() 
{ 
    pArray = (int *)malloc(sizeof(int) * 5); 
} 

MyClass::~MyClass() 
{ 
    free(pArray); 
    pArray = NULL; 
} 

void MyClass::Set() 
{ 
    if (pArray != NULL) 
    { 
     pArray[0] = 0; 
     pArray[1] = 1; 
     pArray[2] = 2; 
     pArray[3] = 3; 
     pArray[4] = 4; 
    } 
} 

void MyClass::Show() 
{ 
    if (pArray != NULL) 
    { 
     for (int i = 0; i < 5; i++) 
     { 
      printf("pArray[%d] = %d\n", i, pArray[i]); 
     } 
    } 
} 

あなたがコードから見ることができるようにSTL関連のものは一切使っていませんでした。 ここに、関数ライブラリのエクスポートのソースファイルがあります。 func.h

#ifdef __cplusplus 
extern "C" { 
#endif 

int SetBabe(int); 
int ShowBabe(int); 

#ifdef __cplusplus 
} 
#endif 

func.cpp

#include <stdio.h> 
#include "myclass.h" 
#include "func.h" 

MyClass cls; 

__attribute__((constructor)) 
static void init() 
{ 

} 

__attribute__((destructor)) 
static void cleanup() 
{ 

} 

int SetBabe(int i) 
{ 
    cls.Set(); 
    return i; 
} 

int ShowBabe(int i) 
{ 
    cls.Show(); 
    return i; 
} 

そして最後に、このライブラリを使用するプログラムの開発のソースコードです。 main.cppに再び

#include <dlfcn.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include "../simple_lib/func.h" 

int main() 
{ 
    void *handle; 
    typedef int (*func)(int); 
    func bbb; 

    printf("start...\n"); 

    handle = dlopen("/data/testt/test.so", RTLD_LAZY); 
    if (!handle) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 

    bbb = (func)dlsym(handle, "SetBabe"); 
    if (bbb == NULL) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 
    bbb(1); 

    bbb = (func)dlsym(handle, "ShowBabe"); 
    if (bbb == NULL) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 
    bbb(1); 

    dlclose(handle); 
    printf("exit...\n"); 

    return 0; 
} 

ライブラリを使用してプログラムを見ることができるようにも任意のSTL関連のものを使用していませんが、プログラムの実行後に、私はmain(...)関数終了時に同じセグメンテーションフォールトを得ました。したがって、問題はSTL自体に関連しておらず、他の場所では隠されています。その後、長い研究の後、私はバグを見つけました。 通常、静的C++変数のdestructorsは、main(...)関数が終了する直前に呼び出されます。メインプログラムで定義されている場合、またはライブラリで定義されていて使用している場合、デストラクタはdlclose(...)の直前で呼び出されます。 Android OSの場合すべての静的C++変数のデストラクタ(メインプログラムまたはライブラリで定義されています)がmain(...)関数終了時に呼び出されます。では、私たちの場合はどうなりますか? cls静的C++変数は、使用しているライブラリで定義されています。その後、main(...)関数の終了直前にdlclose(...)関数を呼び出し、結果ライブラリを閉じ、clsが無効になります。しかし、ポインタclsのどこかに格納されており、デストラクタはmain(...)の関数終了時に呼び出され、コール時にはすでに無効であるため、セグメンテーション違反が発生します。だから解決策はdlclose(...)に電話をかけないことです。残念なことに、このソリューションでは、dlclose(...)呼び出しの結果として呼び出されるため、属性((デストラクタ))を使用して、初期化を解除したいものを初期化できません。

+2

時間の経過とともにAndroidのlibcでデストラクタの処理が数多く修正されているため、正確な動作は使用しているAndroidの特定のバージョンに依存する可能性があります。 b.android.comのバグをサンプルコードとともに提出して、あなたが見ている動作と期待していることを説明することをお勧めします。 – fadden

0

Linuxで同じ頭痛が発生しました。

static void* handle = 0; 
void myDLClose(void) __attribute__ ((destructor)); 
void myDLClose(void) 
{ 
    dlclose(handle); 
} 

int main() 
{ 
    handle = dlopen(...); 
    /* ... real work ... */ 
    return 0; 
} 

dlcloseの誘導性のセグメンテーションフォルトの根本的な原因は次のようになります。私のセグメンテーションフォルトを修正回避策は、()メイン復帰後に呼び出され、)(メインと同じファイルにこれらの行を置くことdlcloseのようですdlclose()の特定の実装では、共有オブジェクト内のグローバル変数はクリーンアップされません。

関連する問題