bugfix> c++ > 投稿

自分のバージョンの __cxa_allocate_exception を実装しようとしていますおよび __cxa_free_exception throw のメモリ割り当てを回避するため 。

だから私はうまく動作するように見えるメモリプールを実装しました。ただし、ネストされた例外を使用してテストする場合、例外のデストラクタはすべての場合に呼び出されたわけではないため、 __cxa_free_exception どちらも呼び出されなかったため、時間の経過とともにメモリプールがいっぱいになりました。

次のサンプルコードを参照してください。

class MyException {
public:
  MyException() {
    std::cout << "MyException constructed." << std::endl;
  }
  ~MyException() {
    std::cout << "MyException destroyed." << std::endl;
  }
};
void * __cxa_allocate_exception(size_t thrown_size)
{
  const auto mem = malloc(thrown_size); //Not part of the example
  std::cout << "allocate: " << mem <<  std::endl;
  return mem;
}
void __cxa_free_exception(void *thrown_object)
{
  std::cout << "free: " << thrown_object << std::endl;
  free(thrown_object); //Not part of the example.
}
void non_rec() {
  try {
    throw MyException();
  } catch(...) {
    try {
      throw MyException();
    } catch(...) {
      //...
    }
  }
}
int main() {
  while(true) {
    non_rec();
    std::cout << "-----------" << std::endl;
  }
}

このプログラムの出力は次のとおりです。

allocate: 0x8cbc20
MyException constructed.
allocate: 0x8cc030
MyException constructed.
MyException destroyed.
free: 0x8cc030
MyException destroyed.
free: 0x8cbc20
-----------
allocate: 0x8cbc20
MyException constructed.
allocate: 0x8cc030
MyException constructed.
MyException destroyed.
free: 0x8cc030
-----------
allocate: 0x8cc030
MyException constructed.
allocate: 0x8cc440
MyException constructed.
MyException destroyed.
free: 0x8cc440
-----------
allocate: 0x8cc440
MyException constructed.
allocate: 0x8cc850
MyException constructed.
MyException destroyed.
free: 0x8cc850

初めて正しく動作します。しかし、その後、2つの例外が構築され、ループの繰り返しごとに割り当てられますが、解放されて破棄されるのは1つだけです。

Ubuntu 16.04でg ++ 5.4.0を使用しています。

回答 2 件
  • これに対する簡単な答えは、それが指定されていないことだと思います。 C ++標準のセクション18.1.4には次のように書かれています。

    The memory for the exception object is allocated in an unspecified way...

    たとえば、MSVCはスタックに割り当てます。幸運を祈ります。

    ただし、記述されたコードが失敗する理由を調べるのは興味深いです(他のコメンテーターに関しては、gccは実行しようとするとメモリ破損を報告します)。@ AlanBirtlesが言うように、答えはここにあります:

    https://code.woboq.org/gcc/libstdc++-v3/libsupc++/eh_alloc.cc.html

    __cxa_allocate_exception の実装を見ると  (279行目)、次の3つのことを行うことがわかります。

    (プライベート)タイプ __cxa_refcounted_exception のヘッダーに余分なスペースを割り当てます

    そのヘッダーをゼロにします

    最初のバイトへのポインタを返します そのヘッダー

    次に、 __cxa_free_exception で 、それはそれを解放する前にそのポインタの調整を可能にします。

    だから、それを機能させるのは十分簡単です、このようなことをしてください(または、 __cxa_refcounted_exception の宣言までトンネルを掘ることができます 、そのサイトのどこかにあると思います):

    #define EXTRA 1024
    extern "C" void * __cxa_allocate_exception(size_t thrown_size)
    {
      void *mem = malloc (thrown_size + EXTRA);
      std::cout << "allocate: " << mem <<  " (" << thrown_size << ") " << std::endl;
      memset (mem, 0, EXTRA);
      return (char *) mem + EXTRA;
    }
    extern "C" void __cxa_free_exception(void *thrown_object)
    {
      std::cout << "free: " << thrown_object << std::endl;
      char *mem = (char *) thrown_object;
      mem -= EXTRA;
      free (mem);
    }
    
    

    そして、Wandboxでこれを実行すると、次の結果が得られます。

    allocate: 0x1e4c990 (1) 
    MyException constructed.
    allocate: 0x1e4ddb0 (1) 
    MyException constructed.
    MyException destroyed.
    free: 0x1e4e1b0
    MyException destroyed.
    free: 0x1e4cd90
    -----------
    allocate: 0x1e4c990 (1) 
    MyException constructed.
    allocate: 0x1e4ddb0 (1) 
    MyException constructed.
    MyException destroyed.
    free: 0x1e4e1b0
    MyException destroyed.
    free: 0x1e4cd90
    -----------
    allocate: 0x1e4c990 (1) 
    MyException constructed.
    allocate: 0x1e4ddb0 (1) 
    MyException constructed.
    MyException destroyed.
    free: 0x1e4e1b0
    MyException destroyed.
    free: 0x1e4cd90
    -----------
    
    

    ただし、これはclangでは機能しないため、別の方法で処理する必要があります。私が言うように、それはUBですので、注意してください。

  • libstdc ++と同様の方法で適切な量のメモリを割り当てると、クラッシュが修正されます。

    #include <iostream>
    #include <cstdlib>
    #include <exception>
    #include <cstring>
    class MyException {
    public:
      MyException() {
        std::cout << "MyException constructed." << std::hex << (size_t)this << std::endl;
      }
      ~MyException() {
        std::cout << "MyException destroyed." << std::hex << (size_t)this << std::endl;
      }
    };
    const size_t __cxa_refcounted_exception_size = 16 * sizeof(size_t); // approx sizeof(__cxa_refcounted_exception)
    void * __cxa_allocate_exception(size_t thrown_size)
    {
      thrown_size += __cxa_refcounted_exception_size;
      const auto mem = malloc(thrown_size);
      std::cout << "allocate: " << mem <<  std::endl;
      memset (mem, 0, __cxa_refcounted_exception_size);
      return (void *)((char *)mem + __cxa_refcounted_exception_size);
    }
    void __cxa_free_exception(void *thrown_object)
    {
      std::cout << "free: " << thrown_object << std::endl;
      char *ptr = (char *) thrown_object - __cxa_refcounted_exception_size;
      free(ptr);
    }
    void non_rec() {
      try {
        throw MyException();
      } catch(...) {
        try {
          throw MyException();
        } catch(...) {
          //...
        }
      }
    }
    int main() {
      for (int i=0;i<4;i++) {
        non_rec();
        std::cout << "-----------" << std::endl;
      }
    }
    
    

    sizeof(__cxa_refcounted_exception_size) の実際の値を見つける必要があります  プラットフォーム用にunwind-cxx.hをインクルードします。

あなたの答え