树莓派1代无法检测到未对齐内存访问

3

我正在执行一系列活动,以确保Redis在一组嵌入式系统中良好运行,包括树莓派。为了修复Redis中执行不对齐内存访问的某些代码路径(由于Redis 3.2引入的更改),我正在尝试强制PI在发生这种情况时记录有关不对齐内存访问的消息或向进程发送信号。通过这种方式,我可以确保Redis在不对齐访问是违规的地方能够良好运行,并且在相反的情况下,在可以执行但速度较慢的平台上运行得更快。 ARM v6(用于PI v1)显然能够处理不对齐的内存访问,因此如果我使用以下命令配置Linux以便向执行不对齐访问的进程发送信号:

echo 4 > /proc/cpu/alignment

然后运行以下程序:

#include <stdio.h>
#include <stdint.h>

int main(int argc, char **argv) {
    char *buf = "foobareklsjdfklsjdfslkjfskdljfskdfjdslkjfdslkjfsd";
    uint32_t *l = (uint32_t*) (buf+1);
    printf("%p\n", l);
    printf("%d\n", (int)*l);
    return 0;
}

我看不到进程接收到任何信号,或者在/proc/cpu/alignment计数器中递增。

我猜测这可能是由于ARM v6能够自动处理不对齐的地址,如果给定的CPU配置标志已设置。我的问题是,我的假设是否正确?如果是,如何强制PI版本1在发生不对齐访问时实际引发异常,以便Linux内核可以捕获它并发送信号、记录访问等,根据/proc/cpu/alignment设置?

编辑:值得注意的是,并非所有指令都能执行不对齐访问,即使在ARM v6中也是如此。例如STMDB、STMFD、LDMDB、LDMEA等多个字的指令确实会引发异常,并被Linux内核捕获。

1个回答

7

我想我最终找到了答案:

  1. Yes I'm correct, up to the word size ARM v6 (or greater) can silently handle the unaligned accesses so no trap is generated and is completely transparent for the Linux kernel. Nothing will be logged, nor the traps counter in /proc/cpu/alignment will be incremented.
  2. AFAIK there is no way I can force the kernel to trap word-sized unaligned accesses, since to do that apparently the CPU should be configured in order to trap the unaligned addresses in every case, but the Linux kernel does not do that AFAIK, probably because there is alignment unsafe code inside the kernel itself. Checking the Linux kernel source code indeed one can see:

    if (cpu_is_v6_unaligned()) {
             set_cr(__clear_cr(CR_A));
             ai_usermode = safe_usermode(ai_usermode, false);
    }
    

    What this means is that the SCTLR.A bit is always cleared, so no trap will be generated for unaligned accesses ARM v6 can handle.

  3. There are a great deal of instructions that will still generate traps when used with unaligned addresses, for example multi store/load instructions, loading and storing of double values.

  4. However, there are instructions that GCC (the version shipped in the default Raspberry Linux distribution) will happily produced that are not handled by the Linux kernel correctly, that will result in a SIGBUS generated even when /proc/cpu/alignment is set to fix the access.
所以第四点基本上意味着,对于 ARM v6,仅仅让 Linux 内核处理不对齐地址是不明智的,即使不对齐地址的性能影响并不是一个问题:由于没有处理所有指令,程序仍然可能崩溃。如何可靠地找到程序中的所有不对齐的访问仍然是一个未解决的问题,因为遗憾的是,否则非常出色的 Valgrind 程序从未实现此功能。过去我不得不使用 QEMU 模拟 Sparc,但这是一个非常缓慢的过程。Valgrind 将是完成此操作的简单方法。

1
ARMv4和v5也允许未对齐的访问,一些ARM核心默认启用,而其他核心则不是。在任何情况下,您都可以启用或禁用它。旧核心对于未对齐的访问行为与人们所期望的不同/奇怪,它会将字节在单词中旋转而不是进入下一个地址。这是完全有道理的,因为这是如何在零字节通道上获取正确的字节进行字节访问(或半字),虽然有多种方法可以解决这个问题。 - old_timer
1
请注意,在Linux下,您可以使x86在发生未对齐访问时陷入陷阱,如果您只想查找软件中的未对齐访问,而不是针对ARM特定情况。 - EOF
EOF是的,如果您指的是AC寄存器的话,我已经发现了。但是,如果不通过gdb设置命令,我没有找到可靠的方法在程序中启用它。 - antirez
@antirez: pushf; orl $0x40000,(%rsp); popf 用于在 x86 上启用对齐检查,pushf; andl $0xFFFBFFFF,(%rsp); popf 用于禁用它。显然,操作系统必须配合,因为 pushf/popf 是可怕的不干净特权分离破坏。 - EOF
请查看 https://hangouts.google.com/hangouts/_/riotmicro.com/acpbringup 中的第1129页。 可以通过在CP15中设置一个位来禁用CPU级别的未对齐支持。 - hesham_EE
EOF在32位版本中使用ESP寄存器而不是RSP寄存器的相同汇编代码时对我无效:它立即崩溃。现在,使用64位版本,它按预期工作了...谢谢,我会尝试理解为什么32位版本没有按预期工作。 - antirez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接