TL;DR我在玩 easm 并烧伤了我的手指。我的限制有意义吗?
当我玩弄内存时,我想测试在 ARM CPU(皮质 A9)上手动读取一些内存
(免责声明:这里的学习目的,当然我同意依赖优化器在 99.999% 的情况下都是正确的做法,但我真的很想理解为什么这里的一切都会爆炸)。
在相关硬件上:
- 总线 CPU - 内存 64 位宽,所以我尝试使用
ldrd
一次加载两个 32b 字的指令。
- 内存中的数据是 128 位对齐的,因此,我们使用两倍
ldrd
操作说明。
我的问题是,生成的汇编器/生成尝试没有意义,这独立于:
- 编译器(使用 GCC 和 clang 进行测试)
- 优化级别(使用-O0 -Og -O2 -O3 测试)
- 跨/本机(使用arm-linux-gnueabihf-gcc和本机gcc进行测试)
这是一个演示该问题的最小示例:
#include <stdint.h>
// custom structure: represent 128 bits
typedef struct __attribute__ ((packed)) u128
{
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
} u128;
int main(void)
{
uint32_t *ptr = (uint32_t*) 0xdeadbeef; // For test purpose, just a random location in memory
u128 words;
// 1st read: 64 bits
asm volatile inline (
"ldrd %[high_32b], %[low_32b], [%[addr]], #8"
: [high_32b] "=X" (words.a), [low_32b] "=X" (words.b)
: [addr] "r" (ptr));
// 2nd read: 64 bits
asm volatile inline (
"ldrd %[high_32b], %[low_32b], [%[addr]], #8"
: [high_32b] "=X" (words.c), [low_32b] "=X" (words.d)
: [addr] "r" (ptr));
return 0;
}
GCC
arm-linux-gnueabihf-gcc -Wall -Wextra -O3 -g -ggdbbroken_asm.c -obroken_asm
/tmp/ccIaxiTz.s:汇编器消息:
/tmp/ccIaxiTz.s:51:警告:基址寄存器写回,并与传输寄存器之一重叠
拆解(radare2 -A -c 's sym.main; pdf' broken_asm
)
│ 0x000003da f3e80221 ldrd r2, r1, [r3], 8
| 0x000003de f3e80232 ldrd r3, r2, [r3], 8 ;
broken_asm.c:27 asm 易失性内联(
所以,确实,这个警告是有道理的:ldrd r3, r2, [r3], 8
似乎坏了
(预期:来源!=目的地。例如:ldrd r3, r2, [r4], 8
)
Clang
clang -mtune=cortex-a9 --target=arm-linux-gnueabihf -isystem /usr/arm-linux-gnueabihf/include -Wall -Wextra -O3 -g -ggdb broken_asm.c -o broken_asm
broken_asm.c:22:5: 错误:Rt 必须是偶数
“ldrd %[high_32b]、%[low_32b]、[%[addr]]、#8”
^ :1:11:注意:在此处实例化为程序集
ldrd r1, r2, [r0], #8
^broken_asm.c:28:5:错误:基址寄存器需要与目标寄存器不同
“ldrd %[high_32b]、%[low_32b]、[%[addr]]、#8”
^ :1:11:注意:在此处实例化为程序集
ldrd r0, r1, [r0], #8
^ 产生 2 个错误。
那么,让我们阅读一些错误消息:
基址寄存器需要与目标寄存器不同
好的,与 GCC 类似的问题(是的,它更像是一个错误而不是警告)
错误:Rt 必须是偶数
等等什么?ldrd r1, r2 ...
第一个操作数确实必须是偶数寄存器,第二个操作数必须是后面的奇数寄存器。
来自 ARM 指令参考:
Rt:第一个目标寄存器。对于 ARM 指令,必须是偶数,而不是 R14。
Rt2:第二个目标寄存器。对于 ARM 指令,必须为 。
我很确定我在 EASM 中做了一些错误的事情(因为它实际上几乎是唯一有效的代码行,所以不难猜测)。
这是我到目前为止所理解的限制:
Output:
据我了解,我想要输出的寄存器是只写的。
‘=’标识一个只能写入的操作数
我开始使用“g”作为约束(相同的效果),但选择“X”来给可能的编译器更多的自由:
'X' 允许任何操作数。
Input:
我在两者中都使用“r”ldrd
从同一个寄存器读取。
我也尝试过“X”,但遇到了同样的问题。
'r' 允许使用寄存器操作数,前提是它位于通用寄存器中。
一些注释,因为这篇文章太短了:/
- 主机:Linux(Debian)
- 目标:Zynq 7000(PS侧:Cortex A9)
- Clang --version:Debian clang 版本 11.0.1-2
- 跨 gcc:arm-linux-gnueabihf-gcc (Debian 10.2.1-6) 10.2.1 20210110
- 本机 gcc:gcc (Debian 10.2.1-6) 10.2.1 20210110
- 调整二进制文件以手动设置操作码中的寄存器似乎按预期工作
所以,我真的不知道我在这里做错了什么。欢迎任何指点。