Почему вы не можете установить указатель инструкции напрямую?
В статье в Википедии о сборке x86 говорится, что "регистр IP не может быть доступен программисту напрямую".
Прямо означает с инструкциями, как MOV и добавить.
Почему бы и нет? В чем причина этого? Каковы технические ограничения?
4 ответа
Вы не можете получить к нему доступ напрямую, потому что нет законного варианта использования. Любое произвольное изменение инструкции eip
очень затруднит прогнозирование ветвлений и, вероятно, откроет целый ряд проблем безопасности.
Вы можете редактировать eip
с помощью jmp
, call
или же ret
, Вы просто не можете напрямую читать или писать в eip
используя обычные операции
настройка eip
в регистр так же просто, как jmp eax
, Вы также можете сделать push eax; ret
, который толкает значение eax
в стек, а затем возвращается (т.е. всплывает и прыгает). Третий вариант call eax
который делает звонок по адресу в eax.
Чтение можно сделать так:
call get_eip
get_eip:
pop eax ; eax now contains the address of this instruction
Это было бы возможным дизайном для x86. ARM выставляет свой программный счетчик для чтения / записи как R15. Это необычно, хотя.
Это позволяет очень компактную функцию пролога / эпилога, а также возможность вставлять или извлекать несколько регистров с помощью одной инструкции: push {r5, lr}
на входе, и pop {r5, pc}
возвращать. (Записать сохраненное значение регистра ссылки в счетчик программ).
Тем не менее, это делает реализацию ARM с высокими показателями производительности и выхода из строя менее удобной, и была исключена для AArch64.
Так что это возможно, но использует один из регистров. 32-разрядный ARM имеет 16 целочисленных регистров (включая ПК), поэтому для кодирования номера в машинном коде ARM требуется 4 бита. Другой регистр почти всегда связан как указатель стека, поэтому ARM имеет 14 целочисленных регистров общего назначения. (LR может быть сохранен в стеке, поэтому он может использоваться и используется как регистр общего назначения внутри тел функций).
Большая часть современного x86 унаследована от 8086. Он был разработан с достаточно компактным кодированием команд переменной длины и всего 8 регистрами, требующими только 3 бита для каждого регистра src и dst в машинном коде.
В оригинальном 8086 они не были универсальными, и относительная SP-адресация невозможна в 16-битном режиме, поэтому по существу 2 регистра (SP и BP) связаны для стекового содержимого. Это оставляет только 6 регистров общего назначения, и если один из них будет ПК вместо общего назначения, это приведет к значительному сокращению доступных регистров, значительно увеличивая количество разливов / перезагрузок в типичном коде.
AMD64 добавил r8-r15 и режим относительной RIP-адресации. lea rsi, [rip+whatever]
и режимы относительной RIP-адресации для прямого доступа к статическим данным и константам - все, что вам нужно для эффективного независимого от позиции кода. Косвенные инструкции JMP вполне достаточны для записи в RIP.
На самом деле ничего нельзя получить, разрешив использовать произвольные инструкции для чтения или записи на ПК, поскольку вы всегда можете сделать то же самое с целочисленным регистром и косвенным переходом. Было бы почти чистым недостатком R15 для x86-64 быть тем же, что и RIP, особенно для производительности архитектуры в качестве цели компилятора. (Рукописные asm странные вещи были уже очень редкой нишей к 2000 году, когда был разработан AMD64.)
Таким образом, AMD64 - это действительно первый случай, когда x86, возможно, получил полноценный счетчик программ, такой как ARM, но было много веских причин не делать этого.
Я думаю, что они имели в виду, что IP-регистр не может быть доступен напрямую так же, как другие регистры. Программисты могут точно писать в IP, например, с помощью инструкции перехода.
jmp
установит EIP
регистр.
этот код установит eip на 00401000:
mov eax, 00401000
jmp eax ;set Eip to 00401000
и для получения EIP
call GetEIP
.
.
GetEIP:
mov eax, [esp]
ret