.ascii 与 .word 的 ARM 字节顺序和字节顺序

ARM endianness and byte ordering for .ascii vs .word

刚开始学习ARM汇编。我目前使用的是带有“GNU 汇编器版本 2.35.2 (arm-linux-gnueabihf)”的 32 位 Raspian。

这是我将部分 ascii 加载到寄存器中的简单程序:

.global _start
_start:
    ldr r1,=helloworld
    ldr r2,[r1]

    @prepare to exit
    mov r0,#0
    mov r7,#1
    svc 0

.data
helloworld:
    .ascii "HelloWorld"

我将它加载到 gdb 中,可以看到我的寄存器 r2 加载了 0x6c6c6548(在 ascii“lleH”中)。快速 objdump 显示:

Contents of section .data:
 0000 48656c6c 6f576f72 6c64               HelloWorld

我有以下问题:

  1. 字符串在内存中是什么样子的?换句话说,字节顺序何时出现?加载到内存中会发生反转吗?或者字符串将按原样加载到内存中,但在加载到寄存器时会反转?
  2. 为什么.word下面程序的寄存器r2的内容是0x12345678而不是0x78563412?为什么后面没有字节顺序?

注意:使用 .word 而不是 .ascii

.global _start
_start:
    ldr r1,=helloworld
    ldr r2,[r1]
    mov r0,#0
    mov r7,#1
    svc 0

.data
helloworld:
    .word 0x12345678

编辑

第一个程序的内存转储显示即使内存中的字符串顺序与源代码和目标文件中的顺序相同:

>>> x/32xb 0x1008c
0x1008c:    0x48    0x65    0x6c    0x6c    0x6f    0x57    0x6f    0x72
0x10094:    0x6c    0x64    0x41    0x11    0x00    0x00    0x00    0x61

这表明 ldr 指令正在将读取的内存转换为小端格式,其中 LSB 保存内存中的第一个字节。理解是否正确?但这仍然没有回答为什么 .word.

没有发生这种情况

字节顺序或字节顺序是组成数字的字节在内存中表示的顺序。

字符串是一个字节数组。这个字符串的每个字节都遵循字节序,但是对于单个字节,小端和大端是一样的。

关于你的第二个问题:字节顺序只会影响存储在内存中的数据。汇编程序为您提供计算机程序的人类可读表示。令牌 0x12345678 表示某个数字。当传输到内存时,这个令牌将以适当的字节顺序写入内存。汇编器会处理这个。

在调试器中观察程序的执行时,您还将看到寄存器内容为 0x12345678。这是因为寄存器不是内存的一部分,不按字节划分。每个寄存器保存一个 32 位数字。 CPU 以配置的字节顺序在寄存器和内存之间传输数据(参见 SETEND 指令)并且如果寄存器不被划分为字节,则没有有意义的方式为其分配字节顺序。调试器只能向您显示它的数值。这就是您在程序中分配给它的值。太疯狂了,是吗?

.ascii 是一个字节串。word 是一个 32 位项目的列表,而不是 8 位项目,它们是无可比拟的。也许你想要 .byte?

.ascii "Hello"
.align
.word 0x12345678
.byte 0x12,0x34,0x56,0x78

assemble 和 disassemble

00000000 <.text>:
   0:   6c6c6548    cfstr64vs   mvdx6, [ip], #-288  ; 0xfffffee0
   4:   0000006f    andeq   r0, r0, pc, rrx
   8:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000
   c:   78563412    ldmdavc r6, {r1, r4, sl, ip, sp}^

link,复制到二进制文件并转储

00000000  48 65 6c 6c 6f 00 00 00  78 56 34 12 12 34 56 78 |Hello...xV4..4Vx|
00000010

到目前为止一切都如预期的那样不足为奇。 ascii 字符串是字节串,我们按声明的顺序查看它们。这个字就是一个字,这是一个小端目标,0x12345678,0x78 是最低有效字节,所以它首先出现在最低地址。为了与 .ascii apples to apples 进行比较,我们需要一串字节,因此首先声明 0x12 就像首先声明 'H' 一样,因此我们首先在内存中看到它。

ldr r0,label0
ldr r1,label1

.ascii "Hello"
.align
label0:
.word 0x12345678
label1:
.byte 0x12,0x34,0x56,0x78

assemble 和 disassemble

00000000 <label0-0x10>:
   0:   e59f0008    ldr r0, [pc, #8]    ; 10 <label0>
   4:   e59f1008    ldr r1, [pc, #8]    ; 14 <label1>
   8:   6c6c6548    cfstr64vs   mvdx6, [ip], #-288  ; 0xfffffee0
   c:   0000006f    andeq   r0, r0, pc, rrx

00000010 <label0>:
  10:   12345678    eorsne  r5, r4, #120, 12    ; 0x7800000

00000014 <label1>:
  14:   78563412    ldmdavc r6, {r1, r4, sl, ip, sp}^

同样不足为奇。 DISASSEMBLER 试图将这些字节转换为指令并将它们显示为单词,因此我们分别看到 0x12345678 和 0x78563412,这些值将落入 r0 和 r1

Link 并复制到二进制和十六进制转储 -C

00000000  08 00 9f e5 08 10 9f e5  48 65 6c 6c 6f 00 00 00  |........Hello...|
00000010  78 56 34 12 12 34 56 78                           |xV4..4Vx|
0

而且我们没有做任何更改,因此输出不会根据数据项发生变化。