LINUX.ORG.RU

asm retf segfault

 


0

2

Привет лор, снова.

В ходе работы над задачей из этого ASM CS:RIP store'n'load треда, образовался следубщий код: https://gist.github.com/JAkutenshi/1486d1eb5e48d1117a03e0db68ad8f7b

Вкратце: сохраняются cs и ip во внешние переменные в первом инлайне, во втором заносятся в стек и вызывается retf. Проверил в gdb: косяков с пересекающимися регистрами нету, все в стеке лежит аккуратно и верно, скриншот прилагается: https://imgur.com/a/y80SATM

retf последовательно забирает со стека первые 8 байт, заносит в rip, вторые в cs, что соответствует переходу на сл. после lea, вторую сверху, строчку. Что должно дать неожиданный 0 в переменной i и завершением программы с кодом 1. Но вместо этого он мне гордо показывает segfault на выполнении retf. ЧЯДНТ?

★★

Последнее исправление: JAkutenshi (всего исправлений: 1)

Не считая того, что надо было бы для корректности эксперимента большую часть операндов использовать из памяти, то решается все ret-ом и передачей только ip. Вот только проблема, что cs-то запросто может меняться, если это какая-то подгружаемая в рантайме библиотека. Если кто-то подскажет по этому моменту, буду очень благодарен. Пока что выглядит все так, что ОС не дает cs менять, насколько я помню там должны быть какие-то методы защиты от подобных хаков. Но подробностей не знаю.

Корректный код прилагается: https://gist.github.com/JAkutenshi/047908d5ba974f710682b90bfe08c9eb

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Вот только проблема, что cs-то запросто может меняться

http://ru.osdev.wikia.com/wiki/Сегментные_регистры

В 64-разрядном режиме сегментные регистры CS, DS, ES и SS в формировании линейного адреса не участвуют, поскольку сегментация в этом режиме не поддерживаются. Сегментные регистры FS и GS могут использоваться в качестве дополнительных регистров базы, о чём подробнее говорится в разделе Эффективный адрес.

SZT ★★★★★
()

push cs не нужен.

mv ★★★★★
()
Ответ на: комментарий от SZT

Да, спасибо, прочитал подробнее. Это обясняет, почему rip был такой конский, а cs 51. Более того, 64битный линукс еще и отводит теоретически столько памяти, что беспокоиться о смене cs нету смысла, даже если там учавствуют какие-то внешние объекты (shared objects например). А retf не сработал, потому что, собственно, сегментация в таком режиме не поддерживается. Пока вся работа в одном процессе - использую ret и не беспокоюсь. Все правильно понял?

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

Пока вся работа в одном процессе - использую ret и не беспокоюсь. Все правильно понял?

Вообще да, но не совсем. Инструкцию ret не рекомендуется так использовать. Рассогласование call/ret будет, это не очень хорошо для производительности.

Тут описано

The P1 has no return stack buffer, but uses the same method for returns as for indirect jumps. Later processors have a return stack buffer. The size of this buffer is 4 in the PMMX, 8in Atom, 12 in AMD k8, 16 in PPro, P2, P3, P4, P4E, PM, Core2 and Nehalem, and 24 in AMD k10. This size may seem rather small, but it is sufficient in most cases because only the innermost subroutines matter in terms of execution time. The return stack buffer may be insufficient, though, in the case of a deeply nesting recursive function.

In order to make this mechanism work, you must make sure that all calls are matched with returns. Never jump out of a subroutine without a return and never use a return as an indirect jump. It is OK, however, to replace a CALL MYPROC/ RETsequence with JMP MYPROC in 16 and 32 bit mode. In 64 bit mode, obeying the stack alignment standard, you can replace SUB RSP,8 / CALL MYPROC / ADD RSP,8 / RET with JMP MYPROC.

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: комментарий от SZT

А, да, это проскальзывало в соседнем треде. Я не мог jmp использовать из-за смещения сегмента, его надо было делать фиксированным при jump far. Поскольку теперь предполагается, что cs не меняется, то ничето не мешает двигаться просто через rip. И таким образом тест будет точнее, ибо не будет ломать branch prediction и накладные расходы снизятся (хотя бы потому что минус 2 пуша, в идеале использовать как можно меньше операторов). Надеюсь правильно все понял. Очередное большое спасибо! Да и тред, вроде, можно закрывать за прояснением деталей.

JAkutenshi ★★
() автор топика
Ответ на: комментарий от JAkutenshi

У меня еще другой вопрос есть - почему б просто не взять setjmp/longjmp? У тебя наверняка в твоей реализации будут еще всякие неочевидные баги, на отладку которых ты потратишь кучу времени

SZT ★★★★★
()
Ответ на: комментарий от JAkutenshi

Еще хотелось бы заметить, что в GCC есть computed goto. https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

Можно сделать свой стек из меток и по ним возвращаться, если дело происходит в пределах одной функции. Вот например простая стековая машина, считающая числа Фибоначчи, работающая по этому принципу: https://wandbox.org/permlink/86FjaMCLkzK9y78t

SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.