1

我试图弄清楚数组在 ARM 汇编中是如何工作的,但我只是不知所措。我想将一个大小为 20 的数组初始化为 0、1、2 等等。

A[0] = 0
A[1] = 1

我什至不知道如何打印我必须查看的内容是否正确。这是我到目前为止所拥有的:

.data
.balign 4       @ Memory location divisible by 4
        string: .asciz "a[%d] = %d\n"
        a:      .skip   80      @ allocates 20
.text
.global main
.extern printf

main:
        push    {ip, lr}        @ return address + dummy register
        ldr     r1, =a          @ set r1 to index point of array
        mov     r2, #0          @ index r2 = 0
loop:
        cmp     r2, #20         @ 20 elements?
        beq     end             @ Leave loop if 20 elements
        add     r3, r1, r2, LSL #2      @ r3 = r1 + (r2*4)
        str     r2, [r3]        @ r3 = r2
        add     r2, r2, #1      @ r2 = r2 + 1
        b       loop            @ branch to next loop iteration
print:
        push    {lr}            @ store return address
        ldr     r0, =string     @ format
        bl      printf          @ c printf
        pop     {pc}            @ return address

ARM 让我很困惑,我不知道我做错了什么。如果有人可以帮助我更好地理解这是如何工作的,那将不胜感激。

4

2 回答 2

4

对于想了解如何在arm 汇编语言中为数组分配内存的其他人,这可能会有所帮助。这里是一个添加相应数组元素并存储在第三个数组中的简单示例。

.global  _start

_start:
      MOV R0, #5              
      LDR R1,=first_array     @ loading the address of first_array[0]
      LDR R2,=second_array    @ loading the address of second_array[0]
      LDR R7,=final_array     @ loading the address of final_array[0]
      MOV R3,#5               @ len of array
      MOV R4,#0               @ to store sum
check: 
      cmp R3,#1               @ like condition in for loop for i>1
      BNE loop                @ if R3 is not equal to 1 jump to the loop label
      B _exit                 @ else exit
loop:
      LDR R5,[R1],#4          @ loading the values and storing in registers and base register gets updated automatically R1 = R1 + 4 
      LDR R6,[R2],#4          @ similarly
      add R4,R5,R6         
      STR R4,[R7],#4          @ storing the values back to the final array 
      SUB R3,R3,#1            @ decrment value just like i-- in for loop
      B check
_exit:
      LDR R7,=final_array     @ before exiting checking the values stored 
      LDR R1, [R7]            @ R1 = 60 
      LDR R2, [R7,#4]         @ R2 = 80
      LDR R3, [R7,#8]         @ R3 = 100
      LDR R4, [R7,#12]        @ R4 = 120
      MOV R7, #1              @ terminate syscall, 1
      SWI 0                   @ execute syscall

.data
first_array:  .word 10,20,30,40  
second_array: .word 50,60,70,80 
final_array:  .word 0,0,0,0,0
于 2018-11-20T10:48:52.070 回答
0

如前所述,您的 printf 有问题,您可以使用工具链本身来查看调用约定是什么,然后遵守该约定。

#include <stdio.h>
unsigned int a,b;
void notmain ( void )
{
  printf("a[%d] = %d\n",a,b);
}

给予

00001008 <notmain>:
    1008:   e59f2010    ldr r2, [pc, #16]   ; 1020 <notmain+0x18>
    100c:   e59f3010    ldr r3, [pc, #16]   ; 1024 <notmain+0x1c>
    1010:   e5921000    ldr r1, [r2]
    1014:   e59f000c    ldr r0, [pc, #12]   ; 1028 <notmain+0x20>
    1018:   e5932000    ldr r2, [r3]
    101c:   eafffff8    b   1004 <printf>
    1020:   0000903c    andeq   r9, r0, ip, lsr r0
    1024:   00009038    andeq   r9, r0, r8, lsr r0
    1028:   0000102c    andeq   r1, r0, ip, lsr #32

Disassembly of section .rodata:

0000102c <.rodata>:
    102c:   64255b61    strtvs  r5, [r5], #-2913    ; 0xb61
    1030:   203d205d    eorscs  r2, sp, sp, asr r0
    1034:   000a6425    andeq   r6, sl, r5, lsr #8

Disassembly of section .bss:

00009038 <b>:
    9038:   00000000    andeq   r0, r0, r0

0000903c <a>:
    903c:   

调用约定通常是 r0 中的第一个参数,r1 中的第二个参数,r2 中的第三个参数直到 r3 然后使用堆栈。这有很多例外,但我们可以在这里看到,通常使用 printf 调用正常工作的编译器想要 r0 中格式字符串的地址。a 的值然后 b 的值分别在 r1 和 r2 中。

您的 printf 在 r0 中有字符串,但是使用该格式字符串的 printf 调用需要三个参数。

上面的代码使用了尾部优化和分支到 printf 而不是调用它并从中返回。如今的 arm 约定更喜欢堆栈在 64 位边界上对齐,因此您可以放置​​一些寄存器,您不必关心在 push/pop 上保留以保持对齐

push {r3,lr}
...
pop {r3,pc}

这样做当然不会伤害您,不这样做可能会或可能不会伤害取决于下游的假设。

假设 r1(标签 a)是字对齐地址,您的设置和循环应该可以正常工作。如果你弄乱了你的字符串,它可能会也可能不会,应该先放一个然后是字符串,或者在 a 之前放另一个对齐语句以确保数组对齐。有一些指令集功能可以简化代码,但它看起来是功能性的。

于 2017-02-28T13:38:18.880 回答