__memcpy_sse2_unaligned - 这是什么意思?
__memcpy_sse2_unaligned - what does this mean in detail?
在我的编译器上工作时我得到了这个错误:
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
我如何获得这里出错的详细信息?我从回溯中知道它是导致它的 memcpy
行,但我如何查看内存是如何对齐的?我怎么知道应该如何对齐?
该项目是一个带有 LLVM 后端的编译器,使用 Zend/PHP 运行时和 OCaml 垃圾收集器,所以有很多地方可能出错。
我怀疑这条线是问题的一部分:
zend_string *str = (zend_string *)caml_alloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), 0);
其中 caml_alloc
在 Zend 源代码中是 pemalloc
。
段错误发生在进行 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] = '[=14=]';
CAMLreturn(result);
}
编辑 2:
因为 valgrind 给我这条线
Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
我想我正在尝试复制已经被释放的东西,这意味着当不再引用某些东西时我没有正确地告诉 OCaml GC。
这个错误告诉你在 memcpy 期间发生了一些不好的事情,可能是空指针或大小错误。
不用理会__memcpy_sse2_unaligned
,它是memcpy的实现细节。 memcpy 有很多针对不同情况优化的不同实现,并在给定上下文的情况下动态分派到最有效的实现。当 sse2 指令可用并且指针未与 16 字节边界对齐(sse2 指令无法加载未对齐的值)时,似乎使用了该指令,这可能是通过一次复制一个字节直到达到 16 字节边界然后切换到快速路径。
至于与 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 没有优化它不应该优化的东西)。
所以这通常意味着将 gcroot 与 OCaml 的 GC 一起使用不是一个好主意。新方法更适合那种 GC,但您仍然需要注意不要跨函数调用或分配访问指针。
因此您的错误可能与此类问题有关:指针在某些时候是有效的,然后在压缩过程中移动了一个值,导致某些 gc 页面未被使用,因此被释放。
在我的编译器上工作时我得到了这个错误:
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
我如何获得这里出错的详细信息?我从回溯中知道它是导致它的 memcpy
行,但我如何查看内存是如何对齐的?我怎么知道应该如何对齐?
该项目是一个带有 LLVM 后端的编译器,使用 Zend/PHP 运行时和 OCaml 垃圾收集器,所以有很多地方可能出错。
我怀疑这条线是问题的一部分:
zend_string *str = (zend_string *)caml_alloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), 0);
其中 caml_alloc
在 Zend 源代码中是 pemalloc
。
段错误发生在进行 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] = '[=14=]';
CAMLreturn(result);
}
编辑 2:
因为 valgrind 给我这条线
Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
我想我正在尝试复制已经被释放的东西,这意味着当不再引用某些东西时我没有正确地告诉 OCaml GC。
这个错误告诉你在 memcpy 期间发生了一些不好的事情,可能是空指针或大小错误。
不用理会__memcpy_sse2_unaligned
,它是memcpy的实现细节。 memcpy 有很多针对不同情况优化的不同实现,并在给定上下文的情况下动态分派到最有效的实现。当 sse2 指令可用并且指针未与 16 字节边界对齐(sse2 指令无法加载未对齐的值)时,似乎使用了该指令,这可能是通过一次复制一个字节直到达到 16 字节边界然后切换到快速路径。
至于与 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 没有优化它不应该优化的东西)。
所以这通常意味着将 gcroot 与 OCaml 的 GC 一起使用不是一个好主意。新方法更适合那种 GC,但您仍然需要注意不要跨函数调用或分配访问指针。
因此您的错误可能与此类问题有关:指针在某些时候是有效的,然后在压缩过程中移动了一个值,导致某些 gc 页面未被使用,因此被释放。