В чем разница между загрузкой данных с помощью оператора = или из пула литералов?

#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 = выберет оптимальную инструкцию.