#assembly #arm
Вопрос:
Итак, я изучаю сборку ARM и столкнулся с двумя способами загрузки данных из памяти.
Способ 1: Использование оператора ‘=’
.section .data
number: .word 0x8
.section .text
.global _start
_start:
LDR R1, =number
LDR R1, [R1]
Способ 2. Использование пула букв
.section .data
number: .word 0x8
.section .text
.global _start
_start:
LDR R1, address_of_number
LDR R1, [R1]
address_of_number: .word number
Оба этих метода делают одно и то же. Они загружаются по адресу number
, который существует в .data
разделе, а затем загружаются в фактическое значение, хранящееся по этому адресу.
Поэтому мне интересно, есть ли разница между этими двумя методами? Я читал, что =
оператор аналогичен *
в C. Итак, какова цель использования пула букв, где address_of_number
существует, для хранения адреса?
Комментарии:
1. Когда вы это сделаете
LDR R1, =number
, адресnumber
автоматически будет помещен ассемблером в пул букв для вас, и=number
затем деталь будет заменена на[PC, #somevalue]
.2. Обратите внимание, что
LDR R1, address_of_number
это также будет преобразовано в относительную нагрузку на ПК. Но в этом случае это значение, которое вы явно туда поместили.3. Я думаю, что основное различие заключается в том, что во втором случае вы можете явно указать, где находится литерал в памяти, в то время как в первом случае ассемблер решает за вас.
Ответ №1:
Во-первых, язык ассемблера специфичен для инструмента, а не для цели. Этот синтаксис поддерживается не всеми ассемблерами ARM, и он не поддерживается в равной степени теми, кто его поддерживает. Насколько я понимаю, gnu является наиболее многофункциональным в отношении такого рода вещей.
Второй….Просто попробуй…
ldr r0,=0x1234567
nop
nop
nop
Disassembly of section .text:
00000000 <.text>:
0: e59f0008 ldr r0, [pc, #8] ; 10 <.text 0x10>
4: e1a00000 nop ; (mov r0, r0)
8: e1a00000 nop ; (mov r0, r0)
c: e1a00000 nop ; (mov r0, r0)
10: 01234567 .word 0x01234567
или
ldr r0,hello
nop
nop
nop
hello: .word 0x12345678
Disassembly of section .text:
00000000 <hello-0x10>:
0: e59f0008 ldr r0, [pc, #8] ; 10 <hello>
4: e1a00000 nop ; (mov r0, r0)
8: e1a00000 nop ; (mov r0, r0)
c: e1a00000 nop ; (mov r0, r0)
00000010 <hello>:
10: 12345678 .word 0x12345678
Идентичный машинный код, никакой разницы.
Но если вы сделаете это
ldr r0,=0x00000104
ldr r0,=0x11000000
ldr r0,=0x20000001
ldr r0,=0x0ffffff0
Disassembly of section .text:
00000000 <.text>:
0: e3a00f41 mov r0, #260 ; 0x104
4: e3a00411 mov r0, #285212672 ; 0x11000000
8: e3a00212 mov r0, #536870913 ; 0x20000001
c: e3e002ff mvn r0, #-268435441 ; 0xf000000f
ассемблер gnu, по крайней мере, выберет для вас другую инструкцию (ldr .. = является псевдо-инструкцией)(другие ассемблеры могут этого не делать и
не обязательно должны это делать).
Но, как и ожидалось, если вы:
ldr r0,hello
nop
nop
nop
hello: .word 0x11000000
вы получаете
Disassembly of section .text:
00000000 <hello-0x10>:
0: e59f0008 ldr r0, [pc, #8] ; 10 <hello>
4: e1a00000 nop ; (mov r0, r0)
8: e1a00000 nop ; (mov r0, r0)
c: e1a00000 nop ; (mov r0, r0)
00000010 <hello>:
10: 11000000 .word 0x11000000
Бывают случаи, когда вы, возможно, захотите точно контролировать, где находится пул
, или вы, возможно, специально захотите увеличить нагрузку на ПК. Или хотите
контролировать все один к одному, как часто надеются с помощью языка ассемблера.
Теперь, если вы знаете, как работает кодировка
mov r0,#0x00000104
mov r0,#0x00000101
test.s: Assembler messages:
test.s:3: Error: invalid constant (101) after fixup
Первый будет кодировать второй не будет (более ранние инструкции arm). А затем вам нужно вернуться и изменить код на ldr r0,=
Это личное предпочтение, если вы начнете с ldr = или закончите с ним. Теперь вам нужно понять, что существует несколько наборов инструкций arm и разные версии архитектуры. Большой палец, большой палец 2 и рука имеют разные правила непосредственного кодирования, поэтому то, что работает для одного из них, не обязательно работает для другого. Но, по крайней мере, для ассемблера gnu, ldr = выберет оптимальную инструкцию.