1

私は現在、Android デバイス用の C++ で共有ライブラリを開発しています。

テストを書いているときに、コード例で関数を呼び出すと、segfault (dlfree) を引き起こす奇妙な動作に出くわしました。

初めに:

  • ライブラリ関数を呼び出すテストは、ライブラリに対して動的にリンクします。
  • Linux および Windows デスクトップ用のライブラリとテストもコンパイルしました。そこでは、セグメンテーション違反を引き起こすことなく実行されます。
  • 静的にリンクすると、segfault は android に表示されません。

サンプルコード

typedef unsigned int DBRuleID;
typedef std::string DBRuleTarget;

struct DBRule {
  DBRuleID id; //int
  DBRuleTarget target; //std::string
};


//segfault variant
bool getRule(DBRuleID id, DBRule& rule) {
  rule.target = "I am causing segfault!";
  return true;
}


//working variant
bool getRule(DBRuleID id, DBRule& rule) {
  //nothing is set
  return true;
}

セグメンテーション違反

Build fingerprint: 'generic/sdk/generic:3.0/HONEYCOMB/104254:eng/test-keys'
pid: 525, tid: 525  >>> /data/local/TestRulesDB <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
r0 deadbaad  r1 0000000c  r2 00000027  r3 00000000
 r4 00000080  r5 aff46658  r6 00013000  r7 00000004
 r8 00000004  r9 00013d3c  10 00000000  fp bec61a14
 ip ffffffff  sp bec61950  lr aff193e9  pc aff15f58  cpsr 00000030
         #00  pc 00015f58  /system/lib/libc.so
         #01  pc 00012d2a  /system/lib/libc.so (dlfree)

編集 - 新しい調査結果

関数に渡される DBRule 構造体が値で初期化されている場合、すべて正常に動作しますが、それ以外の場合はセグメンテーション違反が発生します。

//works
DBRule rule_1 = { 0, "target"};

//works not
DBRule rule_1 = { 0, ""};

//works not
DBRule rule_1;

誰かが私にそれを説明してもらえますか? そして、デフォルトで初期化する最良の方法は何ですか?

質問は

  • 私は何を間違っていますか、何が欠けていますか?
  • ヒープに割り当てられたメモリを複数回削除しようとするメカニズムはありますか?

デスクトップで valgrind を起動しましたが、エラーは表示されません。

前もって感謝します!

4

1 に答える 1

1

問題は、すべての空の std::string オブジェクトが内部ストレージに同じ場所を使用することです。空の std::string の内部ストレージは同じ静的メンバーです。そして、STL はこの場所を使用して、std::string の内部ストレージの割り当てを解除する必要があるかどうかを判断します。

これは、静的にコンパイルされたコードの場合、または動的ライブラリの境界を越えて空の文字列を渡さない場合にうまく機能します。しかし、動的ライブラリを扱っている場合、問題が発生し始めます。実行可能ファイルと各動的ライブラリの両方で、空の std::string の格納場所が異なります。

このコードが実行されると、次のようになります。

rule.target = "I am causing segfault!";

最初に、rule.target の内部ストレージの割り当てが解除されます。rule.target が空の std::string であった場合 (その方法に関係なく)、初期化されたコードのグローバルな空の std::string ストレージを指します。これがライブラリ内で発生しなかった場合、ライブラリは空の文字列ではないと判断し、ストレージの割り当てを解除しようとします。しかし、これはクライアント コードによって静的に割り当てられたため、segfault が発生します。

これを修正する1つの方法は、ライブラリを静的にリンクすることです(発見したように)。もう 1 つは、空の std::string の内部シンボルをエクスポートする C++ ランタイムの共有バージョンを使用することです (クライアントも同じことを行う必要があります)。最終的な解決策は、空の std::string を送受信する可能性がある場合、ライブラリのインターフェイスで直接的または間接的に std::string を使用しないようにすることです。

于 2016-01-20T20:48:11.343 に答える