Если вы скажете ассемблеру где-то собрать произвольные байты, это произойдет.db
- это псевдоинструкция, которая генерирует байты, поэтому mov eax, 60
и db 0xb8, 0x3c, 0, 0, 0
в значительной степени эквивалентны в отношении NASM.Любой из них будет выдавать эти 5 байтов на выход в этой позиции.
Если вы не хотите, чтобы ваши данные декодировались как (часть) инструкций, не помещайте их туда, где они будут достигнутывыполнение.
Так как вы используете NASM 1 , он оптимизирует mov rax,60
в mov eax,60
, поэтому в инструкции нет префикса REX, который вы 'ожидание от источника.
Ваш кодированный вручную префикс REX для mov
заменяет его на mov
на R8D вместо EAX :
41 b8 3c 00 00 00 mov r8d,0x3c
(я проверил с помощью objdump -drwC -Mintel
вместо того, чтобы посмотреть, какой бит есть какой в префиксе REX. Я только помню, что REX.W равен 0x48
. Но 0x41
- это префикс REX.B в x86-64).
Таким образом, вместо системного вызова sys_exit
, ваш код запускается syscall
с EAX = 0, что составляет __NR_read
.(Ядро Linux обнуляет все регистры, кроме RSP, перед запуском процесса и в статически связанном исполняемом файле _start
является истинной точкой входа без кода динамического компоновщика, выполняющегося первым. Таким образом, RAX по-прежнему равен нулю).
$ strace ./rex
execve("./rex", ["./rex"], 0x7fffbbadad60 /* 54 vars */) = 0
read(0, NULL, 0) = 0
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
И затем выполнение падает до значения, равного после syscall
, которое в данном случае составляет 00 00
байтов, которые декодируются как add [rax], al
, и, следовательно, segfault. Вы бы видели это, если бы запускали свой код внутри GDB.
Сноска 1: Если бы вы использовали YASM, который не оптимизирован до размера 32-битного операнда :
В руководствах Intel говорится, что нельзя иметь 2 префикса REX для одной инструкции.Я ожидал ошибку недопустимой инструкции (машинное исключение #UD -> ядро доставляет SIGILL), но мой процессор Skylake игнорирует первый префикс REX и декодирует его как mov rax, sign_extended_imm32
.
Single-step, он рассматривается как одиндлинные инструкции, так что я думаю, Skylake решит обработать его так же, как и в других случаях с несколькими префиксами, в которых действует только последний из типов.(Но помните, что это не будущее, другие процессоры x86 могли бы справиться с этим по-другому.)