__memcpy_sse2_unaligned - what does this mean in detail?
Asked Answered
B

1

20

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 memcpy line that causes it, but how do I see how the memory is aligned? And how do I know how it should be aligned?

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.

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_alloc were pemalloc in the Zend source-code.

The segfault happens when doing 10'000 string concatenations. This is the output from 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] = '\0';

  CAMLreturn(result);
}

Edit 2:

Since valgrind gives me this line

Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd

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.

Bluish answered 11/9, 2015 at 14:38 Comment(7)
Can you show us subsetphp_concat_function?Calmative
When you want help about a bug in your code (which some will argue is not what Stackoverflow is for), always include some code (as short as possible) that produces the bug.Hyland
@Hyland Well, I really needed help to analyse the error message. Guess I could split this question in two, really. But I'll look into it.Duplicity
Side comment, I don't think you need to cast the result of caml_alloc. Its just a wrapper on malloc and you don't need to cast the result of malloc either.Fool
You also need a CAMLlocal1 for that value result.Fool
@EdgarAroutiounian Ah, I noticed that caml_alloc returns a value, not a void pointer. So maybe this line is just plain wrong?Duplicity
I don't want to stay this definitively but my intuition is that both are wrong, the casting and the lack of CAMLlocal1Fool
A
19

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

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.

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.

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:

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')

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).

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.

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.

Algy answered 12/9, 2015 at 18:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.