Как регистр кодируется в инструкции ARM64 mov?

Я хотел бы понять, какой бит в инструкции 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 битной инструкции отвечают за значение регистра.

Спасибо

Последнее значение слева изменено с 0xC52 на 0xCD2. Почему?" Вы должны смотреть на байты. Соответствующий байт — это тот, который изменился с 0x52 на 0xD2, т. е. его старший бит (то есть sf) изменился с выключенного на включенный.

Michael 10.12.2020 12:27

Однако я ожидал, что 528C4101 будет отображаться в режиме с прямым порядком байтов, поэтому я нашел этот инструмент преобразования немного запутанным.

Michael 10.12.2020 12:51

Это. Просто этот инструмент преобразования по умолчанию показывает байты со старшим байтом справа (01,41,8C,52 <-- этот байт).

Michael 10.12.2020 13:21

Спасибо, теперь я понимаю свою ошибку! :)

overtur 10.12.2020 13:37

похоже, вам следует прекратить использовать армконвертер...

old_timer 10.12.2020 16:10
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
2 487
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ваше замешательство полностью сводится к порядку байтов.

Из инструкции:

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 в инструкцию с прямым порядком байтов. Я помещу свой вывод в редактирование. Еще раз спасибо, я ценю вашу помощь.

overtur 10.12.2020 13:42

Инструменты GNU и LLVM понимают это правильно: aarch64-linux-gnu-objdump -d показывает 528c4102 32-битную интерпретацию. llvm-objdump -d показывает 02 41 8c 52, необработанную последовательность байтов. Оба они эквивалентны и не вводят в заблуждение; не вините дизассемблеров, просто armconverter.com который тупо сгруппировал его в 02418C52. Переключатель GDB/LLDB фиксирует его на 528C4102, который он называет «обратным порядком байтов». Но это не последовательность байтов с прямым порядком байтов, здесь нет пробелов, поэтому это 32-битное целое значение. В любом случае, @overtur, вы должны принять этот ответ, нажав на галочку под стрелками голосования.

Peter Cordes 10.12.2020 13:52

Инструменты 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

overtur 10.12.2020 14:15
Ответ принят как подходящий

В соответствии с предыдущими комментариями и ответами

Бит 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.

overtur 11.12.2020 09:53

Другие вопросы по теме