Ассемблер GNU ARM изменяет mov на добавляет? - PullRequest
3 голосов
/ 10 июля 2020

сообщество!

У меня есть следующий однострочный исходный файл с именем first.S

mov R1, R2

Я создаю объектный файл следующим образом:

$ arm-none-eabi-as -mcpu=cortex-m3 -march=armv7 -mthumb -c -o first.o first.S 

Затем я разбираю его.

$ arm-none-eabi-objdump -d first.o

first.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   1c11        adds    r1, r2, #0

Очевидно, что две инструкции (mov и add) в этом случае имеют одинаковый желаемый эффект.

Вопрос, хотя это: почему?

Согласно справочному руководству по архитектуре ARMv7-M, существует несколько кодировок для инструкций регистра mov, однако ассемблер предпочитает кодировать ее как инструкцию add.

Есть ли в какой-либо документации место, где описывалось бы такое решение?

Спасибо!

Ответы [ 2 ]

4 голосов
/ 10 июля 2020

С самого начала для набора команд большого пальца кодировка

0001110xxxnnnddd 

является

adds rd,rn,#xxx

это более эффективная кодировка IF модификация of the flags вам подходит.

Псевдо-инструкция mov rd, rn означает, что флаги могут изменяться (как для рук, так и для asm в документе, очевидно). Так что исходная кодировка в порядке.

Теперь дизассемблер должен выбрать if immed == 0, а затем вывести mov rd, rn vs добавляет rd, rn, # 0, оба являются правильными дизассемблерами.

Теперь mov с одним регистром старшим и одним младшим говорит:

Unlike the low register MOV instruction described in MOV (2) on page A7-73, this instruction does not change the flags.

И теперь он попадает на язык ассемблера, который полностью определяется ассемблером, а не целью (НЕ ARM), и ужасный унифицированный синтаксис и так далее. Так что теперь это становится предметом c специально для инструмента. Ассемблер Gnu для большого пальца не любит, например, добавления (неунифицированный синтаксис, который, как мне кажется, значительно проще использовать для большого пальца), вы выполняете добавление и получаете добавление.

.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1

arm-none-eabi-as so.s -o so.o
so.s: Assembler messages:
so.s:5: Error: instruction not supported in Thumb16 mode -- `adds r0,r1,#0'

.thumb
add r0,r1,#0
mov r0,r1
movs r0,r1

00000000 <.text>:
   0:   1c08        adds    r0, r1, #0
   2:   1c08        adds    r0, r1, #0
   4:   1c08        adds    r0, r1, #0

Однако с movs все в порядке.

0x1c08 = 0x0001110000001000, и это инструкция добавления большого пальца, возвращающаяся к armv4t, когда все это началось.

.syntax unified
.thumb
add r0,r1,#0
mov r0,r1
adds r0,r1,#0
movs r0,r1


   0:   f101 0000   add.w   r0, r1, #0
   4:   4608        mov r0, r1
   6:   1c08        adds    r0, r1, #0
   8:   0008        movs    r0, r1

Итак, теперь в этом случае это другой язык ассемблера (тот же инструмент отличается язык ассемблера)

Итак, этот язык ассемблера уважает add vs add и mov vs movs.

Чтобы выполнить добавление без flah, вам нужна кодировка thumb2. Mov без флагов - это старший регистр mov 0x4608 0100011000001000 0x46xx

добавляет, как всегда, и movs теперь закодирован как сдвиг влево, но вместо дизассемблирования lsl r0, r1, # 0 они вместо этого разбирают как mov r0, r1, больше, чтобы вы могли разжевать, вместо того, чтобы просто дизассемблировать mov в качестве добавления. Почему они не использовали аддов? И вот еще одна проблема с этим: если вы посмотрите на инструкцию нижних регистров mov, по крайней мере, в старой руке, она описывает, что происходит с флагами, которые она показывает кодировкой добавления. Но если вы посмотрите на описание lsl, флаги разные, lsl не является заменой mov с флагами, по крайней мере, как описано в самой долгоживущей ARM ARM (с большим пальцем).

Хорошо, и это имеет смысл, они были полезны в старшей руке. если немедленное значение равно нулю, то нет никакого выполнения, поэтому он описывается как установленный в ноль вместе с подписанным флагом переполнения.

Lsl показывает перенос как неизменный, а не нулевой в одном документе по сравнению с другим. Так что, возможно, со временем произошли какие-то изменения в реализации инструкций или один из ARM ARMS оказался неправильным (что случается часто).

Короткий ответ, mov rd, rn всегда была псевдо-инструкцией, документированной как добавление, дизассемблер может распечатать ее в любом случае, это зависит от дизассемблера.

Язык ассемблера определяется инструмент не является целью, поэтому инструмент определяет, какое решение флага использовать в своем синтаксисе, и может выбирать между добавлением, старшими регистрами mov, кодировкой thumb2 или какой-либо другой кодировкой.

Мы не узнаем, Вопрос почему: зачем выбирать одну кодировку вместо другой, где возможны равные кодировки, часто часто выбирается более короткая (расширение thumb против thumb2) (xor в x86 против mov немедленно с нулем в качестве непосредственного). Но lsl vs add vs sub vs ...

Существуют и другие псевдоинструкции, которые вы найдете либо в документации по arm (документирует язык ассемблера его инструмента в то время), а также псевдоинструкции, которые Ассемблер добавляет в свой ассемблер, например, nop.

.thumb
nop
mov r8,r8
mov r4,r4

00000000 <.text>:
   0:   46c0        nop         ; (mov r8, r8)
   2:   46c0        nop         ; (mov r8, r8)
   4:   1c24        adds    r4, r4, #0

И теперь вопрос в том, почему они просто не распечатали это:

   0:   1c08        mov r0,r1  ; (adds r0, r1, #0)

Мне также нравится, как дизассемблер подразумевает точку с запятой в качестве границы комментария, где ассемблер, как ни странно, не поддерживает это (как и любой другой ассемблер на pl anet (ну, большинство)).

Допустим, дизассемблер не знает, какой ассемблер был создан этот машинный код, поэтому для случаев, когда есть псевдо-инструкция, показывающая оба, было бы неплохо.

0 голосов
/ 10 июля 2020

Как и в https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11 .5007716 , код операции инструкции перемещения также составляет 2 байта, и поэтому нет никакого преимущества в использовании любого из них. https://developer.arm.com/documentation/ddi0337/e/instruction-timing/processor-instruction-timings указывает, что обе инструкции используют 1 цикл ЦП для завершения.

Кодирование T2 https://static.docs.arm.com/ddi0403/eb/DDI0403E_B_armv7m_arm.pdf#G11 .5007716 , однако, будет иметь нулевой байт, что является плохим в некоторых сценариях ios (эксплойты).

Однако я могу представить себе сборку ассемблера тем проще, чем меньше различных кодов операций он создаст.

...