Я хотел бы понять, какой бит в инструкции ARM64 mov отвечает за информацию о регистре. Я компилирую свой код с помощью clang, ориентируясь на архитектуру aarch64.
Например, я получаю эту инструкцию со следующим машинным кодом:
01418C52 MOVZ W1, #0x6208
Глядя на документацию «Справочное руководство по архитектуре Armv8, для профиля архитектуры Armv8-A», страница C6-1123
Rd — это поле, содержащее информацию о регистре, как указано в документации:
Это 32-битное имя регистра назначения общего назначения, закодированное в поле «Rd». Является 64-битным именем регистра назначения общего назначения, закодированным в поле «Rd».
С помощью сайта armconverter меняю значение регистра.
Я получаю следующий код, как и ожидалось:
02418C52 MOVZ W2, #0x6208
Шестнадцатеричное значение слева (наименее значимое) меняется с 0x01 на 0x02. Кажется, что код с прямым порядком байтов, но документация с прямым порядком байтов. Но если я изменю букву регистра с W на X, сдвинется еще один бит.
02418CD2 MOVZ X2, #0x6208
Последнее значение справа изменено с 0xC52 на 0xCD2. Почему ?
>>> bin(0xCD2)
'0b110011010010'
>>> bin(0xC52)
'0b110001010010'
Из документации именно старший бит из поля sf отвечает за выбор регистра исходя из размера непосредственного значения (32b или 64b).
32-bit (sf == 0)
MOVZ <Wd>, #<imm>{, LSL #<shift>}
64-bit (sf == 1)
MOVZ <Xd>, #<imm>{, LSL #<shift>}
Но бит не в правильном месте. Возможно, я использую неправильную документацию. Хотелось бы понять какие поля в 32 битной инструкции отвечают за значение регистра.
Спасибо
Однако я ожидал, что 528C4101
будет отображаться в режиме с прямым порядком байтов, поэтому я нашел этот инструмент преобразования немного запутанным.
Это. Просто этот инструмент преобразования по умолчанию показывает байты со старшим байтом справа (01,41,8C,52 <-- этот байт).
Спасибо, теперь я понимаю свою ошибку! :)
похоже, вам следует прекратить использовать армконвертер...
Ваше замешательство полностью сводится к порядку байтов.
Из инструкции:
B2.6.2 Instruction endianness In Armv8-A, A64 instructions have a fixed length of 32 bits and are always little-endian.
Дизассемблеры, с другой стороны, имеют привычку показывать необработанные байты — для A64 это довольно неудачный выбор, но я предполагаю, что это связано с обработкой наборов команд переменной длины, таких как x86 (_64) и ARM/Thumb, где это имеет смысл.
Короче говоря, когда ваш дизассемблер показывает 01418C52
, это необработанные байты, и их следует читать как 0x528c4101
.
Или отображается графически:
+------+----------+----------+----------+----------+
| Byte | 01 | 41 | 8C | 52 |
+------+----------+----------+----------+----------+
| Bits | 00000001 | 01000001 | 10001100 | 01010010 |
+------+----------+----------+----------+----------+
^ ^
| |
Least significant bit Most significant bit
Именно так работает обратный порядок байтов.
Спасибо, теперь я понял! Я сделал свою домашнюю работу с python, чтобы поместить флаг sf в инструкцию с прямым порядком байтов. Я помещу свой вывод в редактирование. Еще раз спасибо, я ценю вашу помощь.
Инструменты GNU и LLVM понимают это правильно: aarch64-linux-gnu-objdump -d
показывает 528c4102
32-битную интерпретацию. llvm-objdump -d показывает 02 41 8c 52
, необработанную последовательность байтов. Оба они эквивалентны и не вводят в заблуждение; не вините дизассемблеров, просто armconverter.com который тупо сгруппировал его в 02418C52
. Переключатель GDB/LLDB фиксирует его на 528C4102
, который он называет «обратным порядком байтов». Но это не последовательность байтов с прямым порядком байтов, здесь нет пробелов, поэтому это 32-битное целое значение. В любом случае, @overtur, вы должны принять этот ответ, нажав на галочку под стрелками голосования.
Инструменты GNU и LLVM понимают это правильно: aarch64-linux-gnu-objdump -d
показывает 528c4102
, 32-битную целочисленную интерпретацию 4 байтов. llvm-objdump -d показывает 02 41 8c 52
, необработанную последовательность байтов. Оба они эквивалентны и не вводят в заблуждение.
Но https://armconverter.com/ тупо группирует его в 02418C52
(в режиме "GDB" по умолчанию). Это плохо. Если вы хотите вручную закодировать некоторый шелл-код AArch64, вы должны использовать .long 0x528c4102
(на ассемблере с прямым порядком байтов, например, как x86, AArch64 или что-то еще), чтобы получить представление MOVZ W2, #0x6208
.
По соглашению одна строка цифр без пробелов имеет разрядные значения, которые увеличиваются справа налево и представляют собой одно целое значение некоторой ширины. Проблема не в тебе, а в https://armconverter.com/.
armconverter имеет переключатель «GDB/LLDB», который фиксирует его на 528C4102
в режиме LLDB, который он называет «big endian». Но это не последовательность байтов с прямым порядком байтов, здесь нет пробелов, поэтому это 32-битное целое значение. 02418C52
— это целое число, которое вы получите, если интерпретируете 4 байта как с прямым порядком байтов (в противоположность тому, что делает ЦП AArch64), 528C4102
— это правильная интерпретация этих 4 байтов с прямым порядком байтов.
Я думаю, что armconverter использует «big endian», чтобы на самом деле означать «перестановку байтов перед удалением пробелов между байтами». Это глупое злоупотребление терминологией. Опять же, и GNU binutils, и дизассемблеры LLVM понимают это правильно, проблема чисто армконвертера.
Большое спасибо @PeterCordes
В соответствии с предыдущими комментариями и ответами
Бит sf никогда не находится в бите 7, он всегда находится в бите 31 для этой инструкции, представление ARM из опубликованного вами документа является единственным правильным способом просмотра инструкции. Никогда не пытайтесь поменять местами этот вид инструкции. Исправьте данные или, что еще лучше, используйте инструмент, который работает, а не глючит/не работает.
так.с
movz w1,#0x6208
бинутилс гну
aarch64-none-elf-as so.s -o so.o
aarch64-none-elf-objdump -d so.o
so.o: file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <.text>:
0: 528c4101 mov w1, #0x6208
лязг/llvm
clang -c so.s -o so.o
llvm.objdump so.o
Disassembly of section .text:
0000000000000000 <$x.0>:
0: 01 41 8c 52 mov w1, #25096
теперь это отличается от 01418c52, интервал подразумевает, что это байты, а не целое слово, и тогда это может указывать на то, что может быть задействован некоторый порядок следования байтов. Я не согласен с тем, что дизассемблеры обязательно меняют местами байты, они могут, как в этом случае, показывать представление байтов против представления слова или полуслова, да. И тогда, если просмотр полуслова, вы должны знать, в каком порядке они отображаются в памяти/в процессоре:
mov.w r10,r11
0: ea4f 0a0b mov.w r10, r11
В данном случае 0xEA4F — это первая половина инструкции.
И clang/llvm, и binutils используют один и тот же формат файла, как показано, поэтому вы можете дизассемблировать сгенерированный двоичный файл clang/llvm с помощью binutils.
aarch64-none-elf-objdump -d so.o
Disassembly of section .text:
0000000000000000 <.text>:
0: 528c4101 mov w1, #0x6208 // #25096
Существуют разные формы прямого порядка байтов. Как задокументировано для armv8
Если у меня есть 32-битное слово с прямым порядком байтов (по умолчанию/обычный) 0x11223344 по адресу 0x1000, то представление BYTES с прямым порядком байтов
0x1000: 0x44
0x1001: 0x33
0x1002: 0x22
0x1000: 0x11
(не 11223344, это просмотр слова)
для прямого байта представление BYTE одних и тех же данных в то же время
0x1000: 0x44
0x1001: 0x33
0x1002: 0x22
0x1000: 0x11
То же самое, известное как инвариант байта или BE-8. Для armv6 и более поздних версий обратный порядок байтов равен BE-8, байтовый инвариант. (ARMv4 и v5 являются словесно-инвариантными BE-32)
Однако доступ к слову варьируется, как и следовало ожидать:
0x1000: 0x11223344 little endian DATA
0x1000: 0x44332211 big endian DATA
0x1000: 0x11223344 little endian INSTRUCTION fetch
0x1000: 0x11223344 big endian INSTRUCTION fetch
Порядок следования инструкций
В ARMv8-A инструкции A64 имеют фиксированную длину 32 бита и всегда имеют обратный порядок байтов.
Инструмент, который вы используете, просто сломан, и если цель инструмента - собрать и показать вам машинный код или наоборот, и он не может выполнить эту простую задачу (чего он явно не может), то я бы просто избегал сайта, как целое. Если они не могут сделать что-то настолько простое, значит, они недостаточно хорошо понимают набор инструкций. Их переключатель с обратным порядком байтов "gdb" не является решением, это просто еще одна неисправная вещь.
Документация ARM верна, а binutils прост в использовании. clang/llvm немного сложнее, я могу предоставить скрипт сборки, если хотите. Хотя у binutils objdump есть свои проблемы, он по-прежнему остается лучшим набором инструментов для такой работы. Ты можешь легко переключаться между языком ассемблера и машинным кодом.
movz w1,#0x6208
.inst 0x528c4101
aarch64-none-elf-as so.s -o so.o
aarch64-none-elf-objdump -d so.o
so.o: file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <.text>:
0: 528c4101 mov w1, #0x6208 // #25096
4: 528c4101 mov w1, #0x6208 // #25096
(также можно с clang/llvm)
Disassembly of section .text:
0000000000000000 <$x.0>:
0: 01 41 8c 52 mov w1, #25096
4: 01 41 8c 52 mov w1, #25096
Вы можете видеть из сегмента документа, который вы разместили, инструкция начинается с x1010010, который может быть либо 0x52, либо 0xD2, (сломанный) инструмент показывает 02418C52, что быстро указывает, что они, возможно, поменяли местами байты машинного кода (требуется дальнейшее расследование, если вы видите такую вещь так как это может быть глупая удача) если вы не видели 0x52 или 0xD2 в данных, то это не та же инструкция, или есть какая-то другая проблема.
Если вы хотите увидеть машинный код для этой архитектуры, просто используйте binutils или clang/llvm или какой-либо другой простой в использовании, не ломающийся инструмент.
спасибо @old_timer. Я буду осторожен с инструментом, который использовал, и проведу несколько тестов с clang/llvm, objdump и binutils.
Последнее значение слева изменено с 0xC52 на 0xCD2. Почему?" Вы должны смотреть на байты. Соответствующий байт — это тот, который изменился с 0x52 на 0xD2, т. е. его старший бит (то есть
sf
) изменился с выключенного на включенный.