在ARMv6“总线错误”与双打时 [英] 'Bus Error' on ARMv6 when working with doubles

查看:279
本文介绍了在ARMv6“总线错误”与双打时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建对ARMv6一个C ++程序,它与总线错误崩溃。使用GDB我已经追踪到问题源于以下code

 双D = *(*两倍)的pData; pData所+ = sizeof的(的int64_t); //字符*的pData

该方案经过收到的消息,并有提取使用上述code一些双重价值。接收到的信息有一些领域,一些双打有些不是。

在x86架构上这工作正常,但在ARM我得到的总线错误。所以,我怀疑我的问题是数据的比对 - 双场必须在内存ARM架构与字边界

我曾尝试以下的修复程序,没有工作(仍然有错误):

 我的int64_t = *(*的int64_t)的pData;
双D = *((双*)及一);

下面的方法加工的(到目前为止):

 双D = 0;
的memcpy(和D中,pdata,sizeof的(双));

时使用的memcpy'最好的办法?或者,有没有更好的办法?

在我来说,我没有过在缓冲区或消息中的字段的顺序数据的打包控制。

相关问题:的std ::原子<双>上的ARMv7(RPi2)和校准/总线错误


解决方案

  

时使用的memcpy'最好的办法?


在一般它的的只有的正确的做法,除非你是针对单个ABI中没有类型需要比1字节对齐更大。

C ++标准是相当冗长,所以我会引用C标准的前pressing同样的事情,更简洁:


  

一个指针到对象或不完整的类型可被转换成一个指针到一个不同的对象或不完整的类型。如果得到的指针是不正确的指向的类型一致,行为是不确定的。


有它是:未定义行为ever- present幽灵。即使是一个x86编译器是非常清楚允许闯入你的房子和果酱擦到你的头发在你睡觉,而不必加载这些数据你期望的方式,如果其ABI是这么说的。

有一点要注意,虽然是现代的编译器往往是足够聪明的那正确性不一定付出性能为代价。让我们充实了例如code:

 的#include<&string.h中GT;双FUNC(字符*数据){
    双D;
    的memcpy(和D,数据的sizeof D);
    返回D组;
}

...在一个编译器把它:

  $铛-target手臂-march = ARMv6的-mfpu = VFPv3的-mfloat-ABI =硬-O1 -S test.c的
...
FUNC:@ @func
        .fnstart
@#BB 0:
        推{R4,R5,R11,LR}
        子SP,SP,#8
        MOV R2,R0
        LDRB R1,[R0,#3]
        LDRB R3,[R0,#2]
        LDRB R12,[R0]
        LDRB LR,[R0,#1]
        LDRB R4,[R2,#4]!
        ORR R5,R3,R1,LSL#8
        LDRB R3,[R2,#2]
        LDRB R2,[R 2,#3]
        LDRB R0,[R0,#5]
        ORR R1,R12,LR,LSL#8
        ORR R2,R3,R2,LSL#8
        ORR R0,R4,R0,LSL#8
        ORR R1,R1,R5,LSL#16
        ORR R0,R0,R2,LSL#16
        STR R1,[SP]
        STR R0,[SP,#4]
        VPOP {} D0
        弹出{R4,R5,R11,PC}

OK,所以它玩的东西安全与按字节的memcpy ;至少它的内联。但是,嘿,它的ARMv6至少支持未对齐字和半字访问如果CPU配置正确 - 让我们告诉编译器我们酷与

  $铛-target手臂-march = ARMv6的-mfpu = VFPv3的-mfloat-ABI =硬-O1 -S -munaligned访问test.c的
...
FUNC:@ @func
        .fnstart
@#BB 0:
        子SP,SP,#8
        LDR R1,[R0]
        LDR R0,[R0,#4]
        STR R0,[SP,#4]
        STR R1,[SP]
        VPOP {} D0
        BX LR

有我们走,这是你可以只用整数加载字做到最好。现在,如果我们编译它的东西有点新的?

  $铛-target手臂-march = ARMv7的-mfpu =霓虹灯vfpv4 -mfloat-ABI =硬-O1 -S test.c的
...
FUNC:@ @func
        .fnstart
@#BB 0:
        vld1.8 {} D0,[R0]
        BX LR

我可以保证,即使是机器在那里将工作,没有任何不确定的行为,两轮牛车将正确加载未对齐双较少超过一个的指令上。注意NEON是这里的关键球员 - vld1 只要求将对准元件大小的基址,因此对于8位元素它永远不对齐。在更一般的情况下(例如,如果它是一个长长,而不是双击),你可能还需要 -munaligned访问来说服编译器和以前一样。

为了便于比较,我们只是看看大家的喜爱突变孙子的一-70计算器芯片票价,以及:

 铛-O1 -S test.c的
...
FUNC:#@func
##BB 0:
        MOVL 4(%ESP),EAX%
        fldl(%EAX)
        RETL

是的,正确的code还是也像最佳code。

I'm creating a C++ program for ARMv6 which crashes with BUS ERROR. Using GDB I have traced the problem to the following code

double d = *(double*)pData; pData += sizeof(int64_t);  // char *pData

The program goes through a received message and has to extract some double values using the above code. The received message has several fields, some doubles some not.

On x86 architectures this works fine, but on ARM I get the 'bus error'. So, I suspect my problem is alignment of data -- the double fields have to be aligned to word boundaries in memory on the ARM architecture.

I have tried the following as a fix, which did not work (still got the error):

int64_t i = *(int64_t*)pData;
double d = *((double*)&i);

The following worked (so far):

double d = 0;
memcpy(&d, pData, sizeof(double));

Is using 'memcpy' the best approach? Or, is there a better way?

In my case I do not have control over the packing of the data in the buffer or the order of the fields in the message.

Related question: std::atomic<double> on Armv7 (RPi2) and alignment/bus errors

解决方案

Is using 'memcpy' the best approach?

In general it's the only correct approach, unless you're targeting a single ABI in which no type requires greater than 1-byte alignment.

The C++ standard is rather verbose, so I'll quote the C standard expressing the same thing much more succinctly:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined.

There it is: that ever-present spectre of undefined behaviour. Even an x86 compiler is perfectly well allowed to break into your house and rub jam into your hair while you sleep instead of loading that data the way you expect, if its ABI says so.

One thing to note, though, is that modern compilers tend to be clever enough that correctness doesn't necessarily come at the cost of performance. Let's flesh out that example code:

#include <string.h>

double func(char *data) {
    double d;
    memcpy(&d, data, sizeof d);
    return d;
}

...and throw it at a compiler:

$ clang -target arm -march=armv6 -mfpu=vfpv3 -mfloat-abi=hard -O1 -S test.c
...
func:                                   @ @func
        .fnstart
@ BB#0:
        push    {r4, r5, r11, lr}
        sub     sp, sp, #8
        mov     r2, r0
        ldrb    r1, [r0, #3]
        ldrb    r3, [r0, #2]
        ldrb    r12, [r0]
        ldrb    lr, [r0, #1]
        ldrb    r4, [r2, #4]!
        orr     r5, r3, r1, lsl #8
        ldrb    r3, [r2, #2]
        ldrb    r2, [r2, #3]
        ldrb    r0, [r0, #5]
        orr     r1, r12, lr, lsl #8
        orr     r2, r3, r2, lsl #8
        orr     r0, r4, r0, lsl #8
        orr     r1, r1, r5, lsl #16
        orr     r0, r0, r2, lsl #16
        str     r1, [sp]
        str     r0, [sp, #4]
        vpop    {d0}
        pop     {r4, r5, r11, pc}

OK, so it's playing things safe with a bytewise memcpy; at least it's inlined. But hey, ARMv6 does at least support unaligned word and halfword accesses if the CPU is configured appropriately - let's tell the compiler we're cool with that:

$ clang -target arm -march=armv6 -mfpu=vfpv3 -mfloat-abi=hard -O1 -S -munaligned-access test.c
...
func:                                   @ @func
        .fnstart
@ BB#0:
        sub     sp, sp, #8
        ldr     r1, [r0]
        ldr     r0, [r0, #4]
        str     r0, [sp, #4]
        str     r1, [sp]
        vpop    {d0}
        bx      lr

There we go, that's about the best you can do with just integer word loads. Now, what if we compile it for something a bit newer?

$ clang -target arm -march=armv7 -mfpu=neon-vfpv4 -mfloat-abi=hard -O1 -S test.c
...
func:                                   @ @func
        .fnstart
@ BB#0:
        vld1.8  {d0}, [r0]
        bx      lr

I can guarantee that, even on a machine where it would "work", no undefined-behaviour-hackery would correctly load that unaligned double in fewer than one instructions. Note that NEON is the key player here - vld1 only requires the base address to be aligned to the element size, so for 8-bit elements it can never be unaligned. In the more general case (say, if it were a long long instead of a double) you might still need -munaligned-access to convince the compiler as before.

For comparison, let's just see how everyone's favourite mutant-grandchild-of-a-1970s-calculator-chip fares as well:

clang -O1 -S test.c
...
func:                                   # @func
# BB#0:
        movl    4(%esp), %eax
        fldl    (%eax)
        retl

Yup, the correct code still also looks like the best code.

这篇关于在ARMv6“总线错误”与双打时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆