コンパイラで作業しているときに、次のエラーが発生しました。
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
ここで問題が発生した原因の詳細を取得するにはどうすればよいですか?バックトレースからそれを引き起こすのはmemcpy
行であることがわかりますが、メモリがどのように整列されているかをどのように確認できますか?そして、どうすればそれがどのようにどのように整列されるべきかを知ることができますか?
プロジェクトは、OCamlガベージコレクターを備えたZend/PHPランタイムを使用するLLVMバックエンドを備えたコンパイラーであるため、多くの問題が発生する可能性があります。
この行が問題の一部であると思います:
zend_string *str = (zend_string *)caml_alloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), 0);
どこ caml_alloc
はZendソースコードのpemalloc
でした。
Segfaultは10'000文字列の連結を行うときに発生します。これはvalgrindからの出力です:
==7501== Invalid read of size 8
==7501== at 0x4C2F790: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==7501== by 0x4D7E58: subsetphp_concat_function (bindings.c:160)
==7501== by 0x4D7F52: foo (llvm_test.s:21)
==7501== by 0x4D7FA9: main (llvm_test.s:60)
==7501== Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
==7501== at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-AMD64-linux.so)
==7501== by 0x4C2627: do_compaction (in /home/olle/kod/subsetphp/test)
==7501== by 0x4C2735: caml_compact_heap (in /home/olle/kod/subsetphp/test)
==7501== by 0x4D08DF: caml_major_collection_slice (in /home/olle/kod/subsetphp/test)
==7501== by 0x4D2DCF: caml_minor_collection (in /home/olle/kod/subsetphp/test)
==7501== by 0x4D2FBC: caml_check_urgent_gc (in /home/olle/kod/subsetphp/test)
==7501== by 0x4D7C45: subsetphp_string_alloc (bindings.c:90)
==7501== by 0x4D7CEE: subsetphp_string_init (bindings.c:122)
==7501== by 0x4D7DEA: subsetphp_concat_function (bindings.c:149)
==7501== by 0x4D7F52: foo (llvm_test.s:21)
==7501== by 0x4D7FA9: main (llvm_test.s:60)
任意のヒントをいただければ幸いです。
編集:
extern value subsetphp_concat_function(value v1, value v2)
{
CAMLparam2(v1, v2);
zend_string *str1 = Zend_string_val(v1);
zend_string *str2 = Zend_string_val(v2);
size_t str1_len = str1->len;
size_t str2_len = str2->len;
size_t result_len = str1_len + str2_len;
value result = subsetphp_string_init("", result_len, 1);
zend_string *zend_result = Zend_string_val(result);
if (str1_len > SIZE_MAX - str2_len) {
zend_error_noreturn(E_ERROR, "String size overflow");
}
memcpy(zend_result->val, str1->val, str1_len); // This is line 160
memcpy(zend_result->val + str1_len, str2->val, str2_len);
zend_result->len = result_len;
zend_result->val[result_len] = '\0';
CAMLreturn(result);
}
編集2:
Valgrindが私にこの行を与えるので
Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
私はすでに解放されているものをコピーしようとしていると思います。つまり、何かが参照されなくなったときにOCaml GCに正しく通知しません。
このエラーは、memcpyの実行中に何か問題が発生したことを示しています。おそらくnullポインターやサイズのエラーなどです。
___memcpy_sse2_unaligned
_を気にしないでください。これはmemcpyの実装の詳細です。 memcpyには、さまざまなケースに合わせて最適化されたさまざまな実装が数多くあり、コンテキストに応じて最も効率的な実装に動的にディスパッチされます。これは、sse2命令が使用可能で、ポインターが16バイト境界に割り当てられていない場合に使用されるようです(sse2命令は、境界調整されていない値をロードできません)。高速パス。
LLVMにリンクされたOCaml gc固有の詳細については、ヒープポインターの処理方法に十分注意する必要があります。 gcrootメカニズムと新しいステートポイントのどちらを使用しているかはわからないので、gcrootを使用していると思います。
OCaml gcは移動するコレクター(マイナーヒープからメジャーヒープに移動し、圧縮中に移動する)であるため、すべての割り当てがポインターを無効にする可能性があります。つまり、ヒープに割り当てられた値へのフィールドアクセスを因数分解するのは通常安全ではありません。たとえば、これは安全ではありません。
v = field(0, x) r = function_call(...) w = field(0, v)
関数呼び出しは、圧縮をトリガーする可能性のあるいくつかの割り当てを行う可能性があります。
v = field(0, x) r = function_call(...) v' = field(0, x) w = field(0, v')
ちなみに、gcrootメカニズムが移動gcを正しく処理できるかどうかもわかりません(llvmは、すべきでないものを最適化しません)。
そのため、通常、OCamlのGCでgcrootを使用することはお勧めできません。そのようなGCには新しい方法の方が適していますが、関数呼び出しまたは割り当て全体でポインターにアクセスしないように注意する必要があります。
したがって、エラーはその種の問題に関連している可能性があります。ポインタはある時点で有効でしたが、圧縮中に値が移動され、一部のgcページが未使用になり、解放されました。