我正在努力学习一些组装,特别是ARM64。
我正在尝试将一个16位整数数组初始化为某个固定值(123).
我现在拥有的是:
.global _main
.align 2
_main:
mov x0, #0 ; start with a 0-byte offset
mov x1, #123 ; the value to set each 16-bit element to
lsl x1, x1, #48 ; shift this value to the upper 16-bits of the register
loop:
str x1, [sp, x0] ; store the full 64-bit register at some byte offset
add x0, x0, #2 ; advance by two bytes (16-bits)
cmp x0, #10 ; loop until we've written five 16-bit integers
b.ne loop
ldr x2, [sp] ; load the first four 16-bit integers into x2
ubfm x0, x2, #48, #63 ; set the exit status to the leftmost 16-bit integer
mov x16, #1 ; system exit
svc 0 ; supervisor call我预计出口状态是123,但是0。我不明白为什么。
如果我注释掉循环的最后两行,退出状态是123,这是正确的。
有人能解释一下发生了什么事吗?是对齐问题吗?
谢谢
发布于 2021-02-09 21:38:57
假设您在小endian Aaarch64 system上运行您的程序,在给定的循环迭代中,您正在重写您在前一个循环中修改的字节:
实际上,您正在编写字节:0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x7b
来自:sp + x0 + 0
致:每次迭代时的sp + x0 + 7。
初始条件:
(gdb) p/x $sp
$3 = 0x40010000
(gdb) x/12xh 0x40010000
0x40010000: 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
0x40010010: 0x0000 0x0000 0x0000 0x0000
# initializing some memory to 0xff so that we may see what is going on
(gdb) set {unsigned long }(0x40010000) = 0xffffffffffffffff
(gdb) set {unsigned long }(0x40010008) = 0xffffffffffffffff
(gdb) set {unsigned long }(0x40010010) = 0xffffffffffffffff
(gdb) x/12xh 0x40010000
0x40010000: 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff 0xffff
0x40010010: 0xffff 0xffff 0xffff 0xffff在第一个循环之前:
(gdb) p/x$x1
$4 = 0x7b000000000000循环:
# pass #1, after str x1, [sp, x0]
(gdb) x/12xh 0x40010000
0x40010000: 0x0000 0x0000 0x0000 0x007b 0xffff 0xffff 0xffff 0xffff
0x40010010: 0xffff 0xffff 0xffff 0xffff
# pass #2, after str x1, [sp, x0]
(gdb) x/12xh 0x40010000
0x40010000: 0x0000 0x0000 0x0000 0x0000 0x007b 0xffff 0xffff 0xffff
0x40010010: 0xffff 0xffff 0xffff 0xffff
# pass #3, after str x1, [sp, x0]
(gdb) x/12xh 0x40010000
0x40010000: 0x0000 0x0000 0x0000 0x0000 0x0000 0x007b 0xffff 0xffff
0x40010010: 0xffff 0xffff 0xffff 0xffff
# pass #4, after str x1, [sp, x0]
0x40010000: 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x007b 0xffff
0x40010010: 0xffff 0xffff 0xffff 0xffff
# pass #5, after str x1, [sp, x0]
(gdb) x/12xh 0x40010000
0x40010000: 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x007b
0x40010010: 0xffff 0xffff 0xffff 0xffff
# after ldr x2, [sp]:
(gdb) p/x $sp
$2 = 0x40010000
(gdb) p/x $x2
$3 = 0x0
(gdb) 如果您没有在x1中移动值,您的程序就可以正常工作,方法是注释掉lsl x1, x1, #48。
# after ldr x2, [sp]
(gdb) x/12xh 0x40010000
0x40010000: 0x007b 0x007b 0x007b 0x007b 0x007b 0x0000 0x0000 0x0000
0x40010010: 0x0000 0x0000 0x0000 0x0000
(gdb) p/x $x2
$1 = 0x7b007b007b007b
(gdb) 尽管如此,这可能更好地使用斯特拉指令,这样您就可以避免在循环的每一次迭代中编写比您应该编写的字节更多的字节,即16字节而不是2字节。
总之,在一个小终端系统上,常量0x0000000000007b将以7b 00 00 00 00 00 00 00的形式存储在内存中(升序地址),而常量0x7b00000000000000将存储为00 00 00 00 00 00 00 7b。
由于您所做的转换,您正在将0x7b00000000000000而不是0x0000000000007b存储到内存中。
发布于 2021-02-10 06:33:33
“有趣”的方式:
AArch64可以有效地将一个重复模式(任何长度为2的幂)放入64位寄存器中,如果在每个重复中所有设置的位都是连续的。(这是它对按位的布尔指令(如orr x1, xzr, #0x0303030303030303 = mov x1, #...)的直接编码方式)。对于您的123 = 0x7b = 0b1111011,这几乎是正确的。
或者,ldr x1, =0x007B007B007B007B将要求汇编程序为您做这件事;在这种情况下,GAS选择将常量放置在内存附近,并以PC相对寻址模式加载它。
,您可以在将数组存储到堆栈的同时为数组预留空间,方法是使用带有写回寻址模式的存储,使用减去的偏移量更新基本寄存器(在本例中为sp)。这就是AArch64如何有效地实现堆栈“推送”操作的方式。例如,在需要保存某些寄存器的函数中,GCC在函数输入时使用stp x29, x30, [sp, -32]!从SP中减去32个字节,以及在该空间底部的一对寄存器的STore。(哥德波特例子
所以我觉得这个应该能行。这确实是组装的,但我还没有试过运行它。AArch64的标准调用约定维护16字节堆栈对齐,因此这个存储对16字节存储是对齐的。
mov x0, #0x7f007f007f007f
and x0, x0, #~0x0004000400040004 // construct 0x007B repeating
stp x0, x0, [sp, -16]! // push x0 twice
// SP now points at 8 copies of (uint16_t)123, below whatever was on the stack beforestrh循环(存储16位半字)适用于无聊的编译器;当手工编写尽可能少地完成指令时。(这是一个普遍的经验法则,并不总是与性能相关!如果商店是最近的,那么只有部分重叠于前一家商店的大负载可能会导致商店转发货摊。
https://stackoverflow.com/questions/66125355
复制相似问题