C语言 __memcpy_sse2_unaligned - 这详细意味着什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32526042/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 12:07:23  来源:igfitidea点击:

__memcpy_sse2_unaligned - what does this mean in detail?

cocamlmemory-alignmentphp-internals

提问by Olle H?rstedt

While working on my compiler I got this error:

在我的编译器上工作时,我收到了这个错误:

Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33

How do I get details of what went wrong here? I know from the backtrace it's a memcpyline that causes it, but how do I see how the memory is aligned? And how do I know how it shouldbe aligned?

我如何获得这里出了什么问题的详细信息?我从回溯中知道这是一条memcpy导致它的线,但是我如何查看内存是如何对齐的?我怎么知道它应该如何对齐?

The project is a compiler with an LLVM back-end using the Zend/PHP runtime with the OCaml garbage collector, so there's is a lot of things that can go wrong.

该项目是一个带有 LLVM 后端的编译器,使用 Zend/PHP 运行时和 OCaml 垃圾收集器,所以有很多事情可能会出错。

I suspect this line being part of the problem:

我怀疑这条线是问题的一部分:

zend_string *str = (zend_string *)caml_alloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), 0);

where caml_allocwere pemallocin the Zend source-code.

其中caml_alloc分别pemalloc在Zend源代码。

The segfault happens when doing 10'000 string concatenations. This is the output from valgrind:

进行 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)

Any tips appreciated.

任何提示表示赞赏。

Edit:

编辑

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] = '
Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
'; CAMLreturn(result); }

Edit 2:

编辑2

Since valgrind gives me this line

由于 valgrind 给了我这条线

##代码##

I guess I'm trying to copy something that has already been freed, meaning that I don't tell the OCaml GC correctly when something is no longer referenced.

我想我正在尝试复制已经被释放的东西,这意味着当某些东西不再被引用时,我不会正确地告诉 OCaml GC。

采纳答案by Pierre Chambart

This errors tells you that something bad happen during memcpy, probably something like a null pointer or error in the sizes.

这个错误告诉你在 memcpy 期间发生了一些不好的事情,可能是空指针或大小错误。

Don't bother with __memcpy_sse2_unaligned, it is an implementation detail of memcpy. memcpy has a lot of different implementation optimized for the different cases and dispatch dynamically to the most efficient one given the context. That one seems to be used when sse2 instructions are available and pointers are not alligned to 16 bytes boundaries (sse2 instructions cannot load unaligned values), which is probably done by copying one byte at a time until a 16 byte boundary is reached then switching to the fast path.

不用管它__memcpy_sse2_unaligned,它是memcpy的一个实现细节。memcpy 有许多针对不同情况优化的不同实现,并根据上下文动态分派到最有​​效的实现。当 sse2 指令可用且指针未对齐到 16 字节边界(sse2 指令无法加载未对齐的值)时,似乎会使用该方法,这可能是通过一次复制一个字节直到达到 16 字节边界然后切换到快速路径。

As for the OCaml gc specific details linked with LLVM, you need to be quite carefull to how you handle heap pointers. As you don't tell whether you are using the gcroot mechanism or the new statepoints, I will suppose you are using gcroot.

至于与 LLVM 相关的 OCaml gc 特定细节,您需要非常小心处理堆指针的方式。由于您不知道您使用的是 gcroot 机制还是新的 statepoints,我假设您正在使用 gcroot。

Since the OCaml gc is a moving collector (moving from minor heap to major heap, and moving during compaction) every allocation can potentially invalidate a pointer. That means that it is usualy unsafe to factorise field access to heap allocated values. For instance this is unsafe:

由于 OCaml gc 是一个移动收集器(从小堆移动到大堆,并在压缩期间移动)每个分配都可能使指针无效。这意味着将字段访问分解为堆分配值通常是不安全的。例如这是不安全的:

v = field(0, x) r = function_call(...) w = field(0, v)

v = field(0, x) r = function_call(...) w = field(0, v)

the function call could do some allocations that could trigger a compaction.

函数调用可以进行一些可能触发压缩的分配。

v = field(0, x) r = function_call(...) v' = field(0, x) w = field(0, v')

v = field(0, x) r = function_call(...) v' = field(0, x) w = field(0, v')

By the way, I'm not even certain that the gcroot mechanism can correctly handle moving gc (that llvm doesn't optimize things it shouldn"t).

顺便说一句,我什至不确定 gcroot 机制是否可以正确处理移动 gc(llvm 不会优化它不应该优化的东西)。

So that usualy means that it's not a good idea to use gcroot with OCaml's GC. The new way is better for that kind of GC, but you still need to be carefull not to access pointer across function calls or allocations.

所以这通常意味着将 gcroot 与 OCaml 的 GC 一起使用不是一个好主意。新方法更适合这种 GC,但您仍然需要小心不要跨函数调用或分配访问指针。

So your error may be something linked to that kind of problem: the pointer was valid at some point, then a value was moved during compaction that resulted in some gc page being unused, hence freed.

因此,您的错误可能与此类问题有关:指针在某个时候有效,然后在压缩期间移动了一个值,导致某些 gc 页面未使用,因此被释放。