Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libffi - а как это вообще работает? #2

Open
DuratarskeyK opened this issue May 19, 2022 · 34 comments
Open

libffi - а как это вообще работает? #2

DuratarskeyK opened this issue May 19, 2022 · 34 comments

Comments

@DuratarskeyK
Copy link

2 момента:

  1. https://github.com/ilyakurdyukov/e2k-ports/blob/main/libffi-3.4.2-e2k.patch#L128 - выделяем на стэке память и сразу выходим за границы куда-то ниже(?) по стэку. По логике у нас там как раз regs_t ret, но так ли это на самом деле? Нельзя выделить память под sizeof(regs_t) + 8 байт?
  2. https://github.com/ilyakurdyukov/e2k-ports/blob/main/libffi-3.4.2-e2k.patch#L128 - что вообще происходит при конвертации указателя на функцию, которая ничего не возвращает и никаких аргуметов не принимает, в функцию, которая возвращает указатель на структуру и принимает его же. По стандарту так вроде делать вообще нельзя, но возможно lcc это проглатывает? Просто при сборке с оптимизациями это иногда падает с SIGILL на https://github.com/ilyakurdyukov/e2k-ports/blob/main/libffi-3.4.2-e2k.patch#L180 , видимо что-то портится на стэке во время вызова самой функции. Без оптимизаций падений не замечено.
@ilyakurdyukov
Copy link
Owner

  1. (long*)alloca(asize) - ARG_REGS
    Для вызова функции компилятор добавит дополнительные 64 байта аллокации стека от себя (которые не будет никак использовать).
  2. Это не указатель на структуру, а буквально структура из 64 байт. А по сути просто сохранение первых 8-ми регистров.

Я знаю почему может падать, это из-за спекулятивных операций в вызываемой функции, которые помечают регистры как недоступные, и когда они сохраняются в этом месте, то выпадет SIGILL если такие были. Надо подумать, как такое решить. Точно можно решить через проверку rsize на блоки по 4 байта и сделать некрасивый switch на 16 вариантов вызовов (возможно меньше, если структуры выравниваются по 8).

@ilyakurdyukov
Copy link
Owner

Можно попробовать сделать так, главное чтобы компилятор не сохранил ret на стек перед switch, а держал на регистрах как получил из функции. Если структура больше 64 байт, то она сразу на стеке сохраняется и на регистрах не передаётся.

  ret = (*(regs_t(*)(regs_t))fn)(*(regs_t*)frame);

#define COPY4(i) *(int*)(frame + i) = ret.r[i];
#define COPY8(i) frame[i] = ret.r[i];
#define COPY16(i) COPY8(i) COPY8(i+1)
#define COPY32(i) COPY16(i) COPY16(i+2)

  if (rvalue)
    {
      switch((rsize + 3) >> 2)
        {
        case 1: COPY4(0) break;
        case 2: COPY8(0) break;
        case 3: COPY8(0) COPY4(1) break;
        case 4: COPY16(0) break;
        case 5: COPY16(0) COPY4(2) break;
        case 6: COPY16(0) COPY8(2) break;
        case 7: COPY16(0) COPY8(2) COPY4(3) break;
        case 8: COPY32(0) break;
        case 9: COPY32(0) COPY4(4) break;
        case 10: COPY32(0) COPY8(4) break;
        case 11: COPY32(0) COPY8(4) COPY4(5) break;
        case 12: COPY32(0) COPY16(4) break;
        case 13: COPY32(0) COPY16(4) COPY4(6) break;
        case 14: COPY32(0) COPY16(4) COPY8(6) break;
        case 15: COPY32(0) COPY16(4) COPY8(6) COPY4(7) break;
        case 16: COPY32(0) COPY32(4) break;
        }
      memcpy(rvalue, frame, rsize);
    }

@ilyakurdyukov
Copy link
Owner

что вообще происходит при конвертации указателя на функцию, которая ничего не возвращает и никаких аргуметов не принимает, в функцию, которая возвращает указатель на структуру и принимает его же.

Первые аргументы передаются на 8-ми регистрах, это просто значит что компилятор инициализирует данные регистры из этой структуры. Тут тонкая игра на том, что компилятор должен правильно это скомпилировать (поэтому важно запускать тесты от libffi для проверки), но видимо ситуация с SIGILL проходит мимо тестов.

@ilyakurdyukov
Copy link
Owner

Обновил патч, грануляцию по 4 байта невозможно реализовать на Си, но она и не нужна, потому что возвращаемый результат расширяется компилятором до целого регистра.

@DuratarskeyK
Copy link
Author

Хм, фикс похоже не помог, потому что падает там же, где и раньше. В оригинальном патче МЦСТ вроде часть вызова функции на ассемблере написана и она вроде рабочая, а вот касты указателей на функцию из одного типа в другой кажется сломаным. Пример нерабочего - blivet из питона для анаконды например или webkit minibrowser.

@ilyakurdyukov
Copy link
Owner

Еще про ffi_closure_e2k() забыл, там с приходящими регистрами нужно так же делать.

@ilyakurdyukov
Copy link
Owner

Обновил патч, сделал то же самое для ffi_closure, судя по ассемблеру компилятор сделал как надо.

@ilyakurdyukov
Copy link
Owner

ilyakurdyukov commented Jun 21, 2022

Нет, на тестах питона падает, похоже нужна инструкция спекулятивного сохранения в память, даже сохранение находится под условным выполнением и условие не выполнено.

@ilyakurdyukov
Copy link
Owner

Проблему с "грязными" значениями из спекулятивных вычислений решил, патч обновил.

@fedya
Copy link

fedya commented Aug 22, 2022

libffi так и не работает нормально
Пробую с libffi-3.3 и патчем отсюда
ловлю сегфолт у anaconda

Anaconda received signal 04!.
/usr/lib64/python3.8/site-packages/pyanaconda/_isys.so(+0x1a50)[0x4623c0525a50]
<signal handler called>
/usr/lib64/libffi.so.7(ffi_call+0x708)[0x4623bee9e370]
/usr/lib64/python3.8/site-packages/gi/_gi.cpython-38.so(+0x8e898)[0x4623be93e898]
/usr/lib64/python3.8/site-packages/gi/_gi.cpython-38.so(+0x55f38)[0x4623be905f38]
/usr/lib64/libpython3.8.so.1.0(_PyObject_MakeTpCall+0x480)[0x4623afe517e8]
/usr/lib64/libpython3.8.so.1.0(+0x443928)[0x4623b019a928]
/usr/lib64/libpython3.8.so.1.0(_PyEval_EvalFrameDefault+0x176d0)[0x4623b018f4b0]
/usr/lib64/libpython3.8.so.1.0(PyEval_EvalFrameEx+0x90)[0x4623b0177dc8]
/usr/lib64/libpython3.8.so.1.0(_PyFunction_Vectorcall+0x820)[0x4623afe53740]
/usr/lib64/libpython3.8.so.1.0(+0x444148)[0x4623b019b148]
/usr/lib64/libpython3.8.so.1.0(_PyEval_EvalFrameDefault+0x17730)[0x4623b018f510]
/usr/lib64/libpython3.8.so.1.0(PyEval_EvalFrameEx+0x90)[0x4623b0177dc8]
/usr/lib64/libpython3.8.so.1.0(_PyEval_EvalCodeWithName+0x17f0)[0x4623b0196b00]
/usr/lib64/libpython3.8.so.1.0(+0x444148)[0x4623b019b148]
/usr/lib64/libpython3.8.so.1.0(_PyEval_EvalFrameDefault+0x17730)[0x4623b018f510]
/usr/lib64/libpython3.8.so.1.0(PyEval_EvalFrameEx+0x90)[0x4623b0177dc8]
/usr/lib64/libpython3.8.so.1.0(_PyFunction_Vectorcall+0x820)[0x4623afe53740]
/usr/lib64/libpython3.8.so.1.0(+0x444148)[0x4623b019b148]
/usr/lib64/libpython3.8.so.1.0(_PyEval_EvalFrameDefault+0x17730)[0x4623b018f510]

@ilyakurdyukov
Copy link
Owner

Пишите что-ли версию компилятора и опции компиляции, и работает ли с либой пропатченной МЦСТ (если у вас есть доступ). Запускали ли тесты в самом libffi после сборки.

@fedya
Copy link

fedya commented Aug 22, 2022

Лог сборки, тут есть и флаги и оции сборки
https://file-store.rosalinux.ru/api/v1/file_stores/9238cbe3eefe30d13f81515c152624711014b3e3.log?show=true

версия компилятора 1.25.24
спек
https://abf.io/import/libffi/blob/rosa2021.1/libffi.spec

@ilyakurdyukov
Copy link
Owner

--without check

Добавьте проверку в спек, в Альте сделано так:

%check
[ -w /dev/ptmx -a -f /proc/self/maps ] || exit
make -k check

@fedya
Copy link

fedya commented Aug 22, 2022

Тесты падают вот таким образом.

                === libffi tests ===

Schedule of variations:
    unix

Running target unix
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
Using ../../testsuite/config/default.exp as tool-and-target-specific interface file.
Running ../../testsuite/libffi.bhaible/bhaible.exp ...
FAIL: libffi.bhaible/test-callback.c -W -Wall -Wno-psabi -DDGTEST=10 -Wno-unused-variable -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-uninitialized -O2 (test for excess errors)


WARNING: program timed out
FAIL: libffi.bhaible/test-callback.c -W -Wall -Wno-psabi -DDGTEST=11 -Wno-unused-variable -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-uninitialized -O2 (test for excess errors)

Running ../../testsuite/libffi.call/call.exp ...
Running ../../testsuite/libffi.closures/closure.exp ...
ERROR: tcl error sourcing ../../testsuite/libffi.closures/closure.exp.
ERROR: tcl error code NONE
ERROR: lcc: error: no input files
    while executing
"exec $compiler --print-multi-lib"
    (procedure "get_multilibs" line 62)
    invoked from within
"get_multilibs"
    (procedure "g++_include_flags" line 9)
    invoked from within
"g++_include_flags"
    (procedure "default_target_compile" line 58)
    invoked from within
"default_target_compile $source $destfile $type $options"
    (procedure "target_compile" line 6)
    invoked from within
"target_compile $source $dest $type $options"
    (procedure "libffi_target_compile" line 62)
    invoked from within
"libffi_target_compile "$prog" "$output_file" "$compile_type" $options"
    (procedure "libffi-dg-test-1" line 52)
    invoked from within
"libffi-dg-test-1 target_compile $prog $do_what $extra_tool_flags"
    (procedure "libffi-dg-test" line 2)
    invoked from within
"${tool}-dg-test $prog [lindex ${dg-do-what} 0] "$tool_flags ${dg-extra-tool-flags}""
    (procedure "saved-dg-test" line 112)
    invoked from within
"saved-dg-test ../../testsuite/libffi.closures/unwindtest.cc {-W -Wall -Wno-psabi -O0} {}"
    ("eval" body line 1)
    invoked from within
"eval saved-dg-test $args "
    (procedure "dg-test" line 6)
    invoked from within
"dg-test $test $options """
    (procedure "run-many-tests" line 63)
    invoked from within
"run-many-tests $tlist  $additional_options"
    (file "../../testsuite/libffi.closures/closure.exp" line 55)
    invoked from within
"source ../../testsuite/libffi.closures/closure.exp"
    ("uplevel" body line 1)
    invoked from within
"uplevel #0 source ../../testsuite/libffi.closures/closure.exp"
    invoked from within
"catch "uplevel #0 source $test_file_name" msg"
Running ../../testsuite/libffi.complex/complex.exp ...
Running ../../testsuite/libffi.go/go.exp ...

                === libffi Summary ===

# of expected passes            1534
# of unexpected failures        2
# of unresolved testcases       3
# of unsupported tests          1
ERROR: -------------------------------------------
ERROR: in testcase ../../testsuite/libffi.closures/closure.exp
ERROR:  lcc: error: no input files
ERROR:  tcl error code NONE
ERROR:  tcl error info:
lcc: error: no input files
    while executing
"exec $compiler --print-multi-lib"
    (procedure "get_multilibs" line 62)
    invoked from within
"get_multilibs"
    (procedure "g++_include_flags" line 9)
    invoked from within
"g++_include_flags"
    (procedure "default_target_compile" line 58)
    invoked from within
"default_target_compile $source $destfile $type $options"
    (procedure "target_compile" line 6)
    invoked from within
"target_compile $source $dest $type $options"
    (procedure "libffi_target_compile" line 62)
    invoked from within
"libffi_target_compile "$prog" "$output_file" "$compile_type" $options"
    (procedure "libffi-dg-test-1" line 52)
    invoked from within
"libffi-dg-test-1 target_compile $prog $do_what $extra_tool_flags"
    (procedure "libffi-dg-test" line 2)
    invoked from within
"${tool}-dg-test $prog [lindex ${dg-do-what} 0] "$tool_flags ${dg-extra-tool-flags}""
    (procedure "saved-dg-test" line 112)
    invoked from within
"saved-dg-test ../../testsuite/libffi.closures/unwindtest.cc {-W -Wall -Wno-psabi -O0} {}"
    ("eval" body line 1)
    invoked from within
"eval saved-dg-test $args "
    (procedure "dg-test" line 6)
    invoked from within
"dg-test $test $options """
    (procedure "run-many-tests" line 63)
    invoked from within
"run-many-tests $tlist  $additional_options"
    (file "../../testsuite/libffi.closures/closure.exp" line 55)
    invoked from within
"source ../../testsuite/libffi.closures/closure.exp"
    ("uplevel" body line 1)
    invoked from within
"uplevel #0 source ../../testsuite/libffi.closures/closure.exp"
    invoked from within
"catch "uplevel #0 source $test_file_name" msg"
--------------------------------------------------
make[3]: *** [Makefile:452: check-DEJAGNU] Error 1
make[3]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/testsuite'
make[2]: *** [Makefile:528: check-am] Error 2
make[2]: Target 'check' not remade because of errors.
make[2]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/testsuite'
Making check in man
make[2]: Entering directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/man'
make[2]: Nothing to be done for 'check'.
make[2]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/man'
Making check in doc
make[2]: Entering directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/doc'
make[2]: Nothing to be done for 'check'.
make[2]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu/doc'
make[2]: Entering directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu'
make[2]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu'
make[1]: *** [Makefile:1344: check-recursive] Error 1
make[1]: Target 'check' not remade because of errors.
make[1]: Leaving directory '/root/libffi/BUILD/libffi-3.3/e2kv4-rosa-linux-gnu'
make: *** [Makefile:2990: check] Error 2
error: Bad exit status from /var/tmp/rpm-tmp.uIVNn7 (%check)

Я так понимаю по большей части make check отрабатывает нормально
и ничего интересного тут нет, однако анаконда всегда сегфолтится.

@ilyakurdyukov
Copy link
Owner

Может каких-то пакетов не хватает для сборки с проверкой, вроде С++ компилятора.

testsuite/libffi.closures содержит два С++ теста.

Также из спеки Альта:

%{?!_without_check:%{?!_disable_check:BuildRequires: dejagnu, gcc-c++, /proc, /dev/pts}}
%configure --disable-exec-static-tramp

Что один из тестов завис нехорошо.

Есть ли в логах ошибки какое-то указание какую функцию вызывает pyanaconda через FFI?

@fedya
Copy link

fedya commented Aug 22, 2022

%configure --disable-exec-static-tramp
Я спек альта смотрю тут https://packages.altlinux.org/ru/sisyphus_e2k/srpms/libffi/specfiles/ и там этой опции нет

Вот еще наблюдение я взял libffi_3.3-vd7_e2k-8c.deb из PDK 7.2
и подложил бинарники и все запустилось как надо.
Однако в самом pdk патчей никаких в дире с libffi нет
/libffi-3.3

-rw-r--r--@ 1 fdrt  staff    37B  4 авг 16:53 atom.conf
-rw-r--r--@ 1 fdrt  staff   1,2K 17 авг 16:59 libffi.build
-rw-r--r--@ 1 fdrt  staff    93B  4 авг 16:53 meta.txt
-rw-r--r--@ 1 fdrt  staff   120B 17 авг 16:59 x86.sh

опции сборки тоже не хитрые

src_config() {
    autoconf
    econf \
        --enable-debug \
        --libdir="/usr/lib${addP}"
}

Подозреваю что в PDK забыли положить патчей

по остальным пунктам что не хватает чего типа с++
всего хватает.

@ilyakurdyukov
Copy link
Owner

и там этой опции нет

Это в старом спеке нет, который для 3.2 с патчами от МЦСТ (закрытыми под NDA).

Подозреваю что в PDK забыли положить патчей

Не положили потому что у них патчи закрытые, надо требовать и долго ждать пока выложат и если выложат когда-то. Я пытаюсь делать опенсорсные, но секретных знаний иногда не хватает. Если бы я знал что там вызывается через FFI, было бы понятнее как исправить.

Можете через gdb попробовать понять в каком месте падает.

@fedya
Copy link

fedya commented Aug 22, 2022

А вот с gdb большой вопрос
я не понимаю как туда пропихнуть gdb чтобы выяснить что за падение происходит подробнее
а corefile который генерится получается кривой

[New LWP 1578156]
[New LWP 1578201]
.reg section in corefile is damaged
.reg section in corefile is damaged

в результате не понятно как вытащит хороший трейс

@fedya
Copy link

fedya commented Aug 22, 2022

А можете выложить libffi.so* собранные на альте 3.3 версии
Хочу проверить с этими либами запустится ли

@ilyakurdyukov
Copy link
Owner

ilyakurdyukov commented Aug 22, 2022

Я заметил, что ffi_call вызывают используя для результата тип ffi_arg, равный по размеру 8 байт.
И я написал код, что сохраняет в него ровно столько, сколько возвращается, например 1 байт для char.
Но код для x86_64 расширяет числа до 8 байт.
И если код для вызова FFI написан так, что вызываемая функция возвращает менее 8 байт, а использует все 8, то будет работать не правильно. Но тесты такого не проверяют.

Код из теста, для примера:

	ffi_arg result;
	ffi_call(&cif, FFI_FN(doit), &result, values);
	printf ("The result is %d\n", (int)result);

@ilyakurdyukov
Copy link
Owner

Где исходники вашего pyanaconda? У них код слишком меняется в isys, я нашел две версии со значительными различиями.

@fedya
Copy link

fedya commented Aug 22, 2022

git: https://abf.io/import/anaconda бранч rosa2021.1

srpm: http://abf-downloads.rosalinux.ru/rosa2021.1/container/4105557/SRPMS/main/release/anaconda-34.25.0.10-1.47.src.rpm

rpms: http://abf-downloads.rosalinux.ru/rosa2021.1/container/4105557/e2kv4/main/release/

файл /usr/lib64/python3.8/site-packages/pyanaconda/_isys.so
внутри пакета
anaconda-core

@fedya
Copy link

fedya commented Aug 22, 2022

мне тут говорят что в альте актуальная версия для e2k
libffi6-3.2.1-alt3.E2K.1.e2kv5.rpm то есть все та же 3.2.1 с major 6

@ilyakurdyukov
Copy link
Owner

мне тут говорят что в альте актуальная версия для e2k libffi6-3.2.1-alt3.E2K.1.e2kv5.rpm то есть все та же 3.2.1 с major 6

Для Эльбруса (и для большинства архитектур) не существует такого пакета, только для riscv64 и mipsel:

https://packages.altlinux.org/ru/sisyphus_mipsel/srpms/libffi6/specfiles/

И я не знаю зачем он вообще создан, для этих архитектур это старая версия, а не актуальная.

@ilyakurdyukov
Copy link
Owner

Лучше дайте мне свой libffi собранный с патчем, я дизассемблирую и посмотрю это место:

/usr/lib64/libffi.so.7(ffi_call+0x708)[0x4623bee9e370]

@ilyakurdyukov
Copy link
Owner

ilyakurdyukov commented Aug 23, 2022

Я обновил патч на основе моих предположений что это могло быть.

Одно из предположений неверное, второе с расширением целых чисел - вряд ли влияет, так что наверное будет падать так же. Мне надо стектрейс падения и библиотеку от одной и той же компиляции библиотеки.

@fedya
Copy link

fedya commented Aug 23, 2022

либа в рпм
http://abf-downloads.rosalinux.ru/rosa2021.1/repository/e2kv4/main/release/lib64ffi7-3.3-3-rosa2021.1.e2kv4.rpm

Обновленный патч сейчас наложу и попробую запустить.

Попробовал, не помогло

Anaconda received signal 04!.
/usr/lib64/python3.8/site-packages/pyanaconda/_isys.so(+0x1a50)[0x4618a6617a50]
<signal handler called>
/usr/lib64/libffi.so.7(ffi_call+0x718)[0x4618a4f807c0]
/usr/lib64/python3.8/site-packages/gi/_gi.cpython-38.so(+0x8e898)[0x4618a4a0b898]
/usr/lib64/python3.8/site-packages/gi/_gi.cpython-38.so(+0x55f38)[0x4618a49d2f38]
/usr/lib64/libpython3.8.so.1.0(_PyObject_MakeTpCall+0x480)[0x461895ef77f8]
/usr/lib64/libpython3.8.so.1.0(+0x443938)[0x461896240938]

На всякий случай вот еще работающей libffi.so.7 от м ц с т
https://file-store.rosalinux.ru/download/3ba89c8dd1e48ed8a36b57e0ee8e13149082482e
от него если либу подложить то все ок

@ilyakurdyukov
Copy link
Owner

Место падения выглядит странно, это сохранение в стек возвращаемого значения после вызова функции.

Указатель неправильным быть не может, значит возвращаемое значение было спекулятивным с тегом указывающим ошибку. Что может случиться когда вызывают функцию что не возвращает аргументов, и от неё приходит какой-то мусор.

Да, с кодом МЦСТ это будет работать, потому что они сохраняют возврат инструкцией что не кидает ошибку на тегах указывающих ошибку.

Я сейчас переписал кусок ffi_call на ассемблер и тестирую такую версию. В такой ситуации это должно работать так же как в коде МЦСТ. Вот только тут может баг в anaconda.

@fedya
Copy link

fedya commented Aug 23, 2022

Можно мне патч? Я быстро протестирую.

@ilyakurdyukov
Copy link
Owner

Вот предварительный патч https://pastebin.com/FSPBatTw
Тесты libffi проходит, тоже проверяйте сначала на них.

@fedya
Copy link

fedya commented Aug 23, 2022

А помогло!

Please make a selection from the above ['c' to continue, 'q' to quit, 'r' to
refresh]: 2
================================================================================
================================================================================
Installation

1) [x] Language settings                 2) [x] Time settings
       (English (United States))                (Europe/Moscow timezone)
3) [!] Installation Destination          4) [ ] Network configuration
       (Processing...)                          (No network devices available)
5) [x] Root password                     6) [!] User creation
       (Root account is disabled.)              (No user will be created)

Please make a selection from the above ['b' to begin installation, 'h' to help,
'q' to quit, 'r' to refresh]:
An unknown error has occured, look at the /tmp/anaconda-tb* file(s) for more details

@ilyakurdyukov
Copy link
Owner

Пару макросов переименовал и закоммитил этот патч.

@fedya
Copy link

fedya commented Aug 27, 2022

А можешь дать свой тг? У меня есть кое-что, что тут выложить не могу.

@ilyakurdyukov
Copy link
Owner

Почта в патче написана, если "тг" означает Телеграм, то там меня нет и не будет.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants