memcpy sse2 unaligned-что это значит в деталях?
во время работы над моим компилятором я получил эту ошибку:
Program received signal SIGSEGV, Segmentation fault.
__memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
как я могу получить подробную информацию о том, что пошло не так здесь? Я знаю из backtrace это memcpy
линия, которая вызывает это, но как я вижу, как выровнена память? И откуда я знаю, как это!--11-->должны быть выровнены?
проект является компилятором с LLVM back-end, использующим среду выполнения Zend/PHP с сборщиком мусора OCaml, поэтому есть много вещей, которые могут пойти не так.
I подозреваю, что эта строка является частью проблемы:
zend_string *str = (zend_string *)caml_alloc(ZEND_MM_ALIGNED_SIZE(_STR_HEADER_SIZE + len + 1), 0);
здесь caml_alloc
были pemalloc
в исходном коде Zend.
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] = '';
CAMLreturn(result);
}
Edit 2:
так как вальгринд дает мне эту строку
Address 0x61db938 is 2,660,600 bytes inside a block of size 3,936,288 free'd
думаю, я пытаюсь что-то скопировать это уже было освобождено, что означает, что я не говорю OCaml GC правильно, когда что-то больше не упоминается.
1 ответов
эти ошибки говорят вам, что что-то плохое происходит во время memcpy, вероятно, что-то вроде нулевого указателя или ошибки в размерах.
не стоит __memcpy_sse2_unaligned
, это деталь реализации memcpy. memcpy имеет множество различных реализаций, оптимизированных для разных случаев и динамически отправляемых в наиболее эффективный с учетом контекста. Кажется, что он используется, когда инструкции sse2 доступны, а указатели не выровнены по границам 16 байтов (sse2 инструкции не могут загружать несогласованные значения), что, вероятно, делается путем копирования одного байта за раз, пока не будет достигнута граница 16 байтов, а затем переключение на быстрый путь.
Что касается конкретных деталей OCaml gc, связанных с LLVM, вам нужно быть очень внимательным к тому, как вы обрабатываете указатели кучи. Как вы не говорите того, используете ли вы механизм gcroot или новый statepoints, я предполагаю, что вы используете 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 не оптимизирует вещи, которые он должен " t).
Так что обычно означает, что не стоит использовать gcroot с GC OCaml. Новый способ лучше для такого рода GC, но вам все равно нужно быть осторожным, чтобы не получить доступ к указателю через вызовы функций или распределения.
таким образом, ваша ошибка может быть связана с такой проблемой: указатель был действителен в какой-то момент, затем значение было перемещено во время уплотнения, что привело к неиспользуемой странице gc, следовательно, освобожденной.