Какое состояние видимого программного процессора должно быть в jmp_buf на процессоре x86-64?
Как уже говорилось, какое видимое программно состояние процессора должно идти в jmp_buf
на процессоре x86-64, когда setjmp(jmp_buf env)
называется? Какого состояния процессора нет?
Я много читал о setjmp
а также longjmp
но не смог найти четкого ответа на мой вопрос. Я знаю, что это зависит от реализации, но я хотел бы знать для архитектуры x86_64.
Из следующей реализации кажется, что на машине с архитектурой x86-64 все сохраняемые регистры вызываемого абонента (%r12-%r15
, %rbp
, %rbx
) должны быть сохранены, а также указатель стека, счетчик программы и все сохраненные аргументы текущей среды. Однако я не уверен в этом, надеюсь, кто-то может прояснить это для меня.
2 ответа
Например, какие регистры x86-64 необходимо сохранить? А как насчет флагов условий? Например, я думаю, что регистры с плавающей запятой не нужно сохранять, потому что они не влияют на состояние программы.
Это из-за соглашения о вызовах. setjmp
это вызов функции, который может возвращаться несколько раз (в первый раз, когда вы на самом деле вызываете его, позже, когда вызывается дочерняя функция longjmp
), но это все еще вызов функции. Как и при любом вызове функции, компилятор предполагает, что все регистры с ограниченным вызовом были перекрыты, поэтому longjmp
не нужно их восстанавливать.
Так что да, они не являются частью "состояния программы" на границе вызова функции, потому что сгенерированный компилятором asm определенно не сохраняет в них никаких значений.
Вы смотрите на реализацию glibc для x86-64 System V ABI, где все векторные / x87-регистры имеют блокировку вызовов и, следовательно, не должны быть сохранены.
В соглашении о вызовах Windows x86-64 xmm6-15 сохраняются для вызова (только младшие 128 битов, а не верхние части y/zmm6-15) и должны быть частью jmp_buf
,
то есть здесь важна не архитектура процессора, а соглашение о вызове программного обеспечения.
Помимо регистров с сохранением вызовов, одним из ключевых моментов является то, что longjmp
к jmp_buf
сохраняется родительской функцией, а не произвольной функцией после вызывающей функции setjmp
вернулся.
Если setjmp
должен был поддерживать это, он должен был бы сохранить весь кадр стека или фактически (чтобы функция могла возвращать, и этот родительский элемент мог возвращать и т. д.) весь стек до самого верха. Это явно безумие, и поэтому понятно, почему longjmp
имеет ограничение на возможность перехода только к родительским / (великим) функциям дедушки и бабушки, поэтому ему просто нужно восстановить указатель стека, чтобы он указывал на все еще существующий кадр стека, и восстановить любые локальные переменные в этой функции, которые могли быть изменены с setjmp
,
(В реализациях C / C++ в архитектурах / соглашениях о вызовах, в которых используется что-то отличное от обычного стека вызовов, аналогичный аргумент о возможности функции jump-target, которую можно вернуть, все еще применяется.)
Как jmp_buf
это единственное место, которое можно использовать для восстановления состояния процессора на longjmp
, это вообще все что нужно для полного восстановления состояния машины как было когда setjmp
называется.
Это, очевидно, очень сильно зависит от процессора и компилятора (что именно он использует функции процессора для хранения состояния программы):
- На идеальной машине с чистым стеком, которая содержит информацию о состоянии процессора нигде, кроме стека, это будет только указатель стека. За исключением очень старых или чисто академических реализаций, такие машины существуют редко. Однако вы можете написать компилятор на современном компьютере, таком как x86, который использует стек исключительно для хранения такой информации. Для такого гипотетического компилятора достаточно сохранить только указатель стека для восстановления состояния программы.
- На более распространенной и практичной машине это может быть указатель стека и полный набор регистров, используемых для хранения состояния программы.
- В некоторых процессорах, которые хранят информацию о состоянии программы в других местах, например на нулевой странице, и в компиляторах, использующих такие функции процессора,
jmp_buff
также потребуется хранить копию этой нулевой страницы (некоторые процессоры 65xx или микроконтроллеры ATmel AVR и их компиляторы могут использовать эту функцию)