Raspberry pi Raspberry PI版本1未检测到未对齐的访问

Raspberry pi Raspberry PI版本1未检测到未对齐的访问,raspberry-pi,arm,memory-alignment,Raspberry Pi,Arm,Memory Alignment,我正在执行一系列活动,以确保Redis在一系列嵌入式系统中运行良好,包括Raspberry PI。为了修复执行未对齐内存访问的Redis的某些代码路径(由于Redis 3.2中引入的更改),我试图强制PI记录未对齐内存访问的消息,或者在发生这种情况时向进程发送信号。通过这种方式,我既可以确保Redis在未对齐的访问发生冲突时运行良好,也可以确保它在可以执行此类访问但速度较慢的平台上运行更快。PI v1中使用的ARM v6显然能够处理未对齐的内存访问,因此如果我使用以下命令配置Linux,以便向执

我正在执行一系列活动,以确保Redis在一系列嵌入式系统中运行良好,包括Raspberry PI。为了修复执行未对齐内存访问的Redis的某些代码路径(由于Redis 3.2中引入的更改),我试图强制PI记录未对齐内存访问的消息,或者在发生这种情况时向进程发送信号。通过这种方式,我既可以确保Redis在未对齐的访问发生冲突时运行良好,也可以确保它在可以执行此类访问但速度较慢的平台上运行更快。PI v1中使用的ARM v6显然能够处理未对齐的内存访问,因此如果我使用以下命令配置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;
}
#包括
#包括
int main(int argc,字符**argv){
char*buf=“foobareklsjdfklsjdfslkjfskdljfskdfjdslkjfdslkjfsd”;
uint32_t*l=(uint32_t*)(buf+1);
printf(“%p\n”,l);
printf(“%d\n”,(int)*l);
返回0;
}
我看不到进程接收到的任何信号,也看不到
/proc/cpu/alignment
处的计数器在递增

我的猜测是,这是因为如果设置了给定的CPU配置标志,ARMV6能够自动处理未对齐的地址。我的问题是,我的假设正确吗?如果是这样,如何强制PI版本1在未对齐的访问情况下实际引发异常,以便Linux内核可以根据/proc/cpu/alignment设置捕获它并发送信号、记录访问等等


编辑:值得注意的是,即使在ARMV6中,也不是所有指令都可以执行未对齐的访问。例如,STMDB、STMFD、LDMDB、LDMEA和类似的多字指令确实会引发异常并被Linux内核捕获。

我想我最终找到了答案:

  • 是的,我是正确的,最大字数的ARMV6(或更高版本)可以静默地处理未对齐的访问,因此不会生成陷阱,并且对于Linux内核是完全透明的。不会记录任何内容,也不会增加
    /proc/cpu/alignment
    中的陷阱计数器
  • AFAIK我无法强制内核捕获字大小的未对齐访问,因为要做到这一点,显然CPU应该进行配置,以便在任何情况下捕获未对齐的地址,但Linux内核不会这样做AFAIK,可能是因为内核本身中存在对齐不安全的代码。检查Linux内核源代码确实可以看到:

    if (cpu_is_v6_unaligned()) {
             set_cr(__clear_cr(CR_A));
             ai_usermode = safe_usermode(ai_usermode, false);
    }
    
    这意味着SCTLR.A位总是被清除,因此没有陷阱 将为ARM v6可以处理的未对齐访问生成

  • 有很多指令在与未对齐的地址一起使用时仍然会生成陷阱,例如多存储/加载指令、加载和存储双值

  • 但是,GCC(默认Raspberry Linux发行版中提供的版本)会很高兴地生成指令,但Linux内核无法正确处理这些指令,即使将
    /proc/cpu/alignment
    设置为修复访问,也会生成SIGBUS
  • 因此,第4点基本上意味着,修复在ARMV6中运行的程序并不是一个好主意,只是让Linux内核为我们处理未对齐的地址,即使未对齐地址的性能影响不是一个问题:程序仍然可能崩溃,因为没有处理所有指令


    如何在一个程序中可靠地找到所有未对齐的访问,对于AIK来说仍然是一个开放的问题,因为不幸的是,原本很好的valgrind程序从未实现过这个特性。在过去,我不得不使用QEMU模拟Sparc,但是这是一个非常缓慢的过程。Valgrind将是实现这一点的简单方法。

    ARMv4和v5还允许未对齐的访问,一些arm内核默认启用它,另一些则不启用,在这两种情况下,您都可以启用或禁用它。未对齐访问的旧内核行为与人们所期望的不同/奇怪,它将在字中旋转字节,而不是插入下一个地址。这非常有意义,因为这是在零字节通道上获取正确字节进行字节访问(或半字)的方式,但这不仅仅是剥猫皮的一种方式。请注意,如果您只想在软件中找到一般的未对齐访问,可以让x86在Linux下捕获未对齐的访问,如果是的,我发现,如果你指的是交流寄存器。然而,如果不是通过gdb set命令,我并没有找到一种可靠的方法在我的程序中启用它。@antrez:
    pushf;orl$0x40000,(%rsp);popf
    在x86上启用对齐检查,
    pushf;andl$0xFFFBFFFF,(%rsp);popf
    来禁用它。显然,操作系统必须合作,因为
    pushf/popf
    是可怕的不干净的特权分离破坏。未对齐支持中的检查页1129可以通过在CP15中设置一个位在CPU级别禁用。