为什么这个 EXC_BAD_ACCESS 发生在 long long 而不是 int? [英] Why does this EXC_BAD_ACCESS happen with long long and not with int?

查看:23
本文介绍了为什么这个 EXC_BAD_ACCESS 发生在 long long 而不是 int?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个带有处理数据序列化的代码的 EXC_BAD_ACCESS.该代码仅在设备(iPhone)上失败,而在模拟器上失败.它也仅在某些数据类型上失败.

I've run into a EXC_BAD_ACCESS with a piece of code that deals with data serialization. The code only fails on device (iPhone) and not on simulator. It also fails only on certain data types.

这是重现问题的测试代码:

Here is a test code that reproduces the problem:

template <typename T>
void test_alignment() {
    // allocate memory and record the original address
    unsigned char *origin;
    unsigned char *tmp = (unsigned char*)malloc(sizeof(unsigned short) + sizeof(T));
    origin = tmp;

    // push data with size of 2 bytes
    *((unsigned short*)tmp) = 1;
    tmp += sizeof(unsigned short);

    // attempt to push data of type T
    *((T*)tmp) = (T)1;

    // free the memory
    free(origin);
}

static void test_alignments() {
    test_alignment<bool>();
    test_alignment<wchar_t>();
    test_alignment<short>();
    test_alignment<int>();
    test_alignment<long>();
    test_alignment<long long>();   // fails on iPhone device
    test_alignment<float>();
    test_alignment<double>();      // fails on iPhone device
    test_alignment<long double>(); // fails on iPhone device
    test_alignment<void*>();
}

猜测一定是内存对齐问题,我决定要彻底了解这个问题.根据我对内存对齐的(有限)理解,当 tmp 前进 2 个字节时,对于对齐大于 2 个字节的数据类型,它变得未对齐:

Guessing that it must be a memory alignment issue, I decided I want to understand the problem thoroughly. From my (limited) understanding of memory alignment, when tmp gets advanced by 2 bytes, it becomes misaligned for data types whose alignment is greater than 2 bytes:

    tmp += sizeof(unsigned short);

但是对于int 和其他人来说,测试代码执行得很好!它只对 long longdoublelong double 失败.

But the test code is executed alright for int and others! It only fails for long long, double and long double.

检查每种数据类型的大小和对齐方式发现失败的数据类型是那些具有不同 sizeof__alignof 值的数据类型:

Examining the size and alignment of each data type revealed that the failing data types are the ones that have different sizeof and __alignof values:

iPhone 4:
bool           sizeof = 1 alignof = 1
wchar_t        sizeof = 4 alignof = 4
short int      sizeof = 2 alignof = 2
int            sizeof = 4 alignof = 4
long int       sizeof = 4 alignof = 4
long long int  sizeof = 8 alignof = 4 // 8 <> 4
float          sizeof = 4 alignof = 4
double         sizeof = 8 alignof = 4 // 8 <> 4
long double    sizeof = 8 alignof = 4 // 8 <> 4
void*          sizeof = 4 alignof = 4

iPhone Simulator on Mac OS X 10.6:
bool           sizeof = 1 alignof = 1
wchar_t        sizeof = 4 alignof = 4
short int      sizeof = 2 alignof = 2
int            sizeof = 4 alignof = 4
long int       sizeof = 4 alignof = 4
long long int  sizeof = 8 alignof = 8
float          sizeof = 4 alignof = 4
double         sizeof = 8 alignof = 8
long double    sizeof = 16 alignof = 16
void*          sizeof = 4 alignof = 4

(这些是从 "C++ 数据对齐和可移植性")

有人可以启发我是什么导致了错误?差异真的是EXC_BAD_ACCESS的原因吗?如果是这样,通过什么机制?

Can someone enlighten me what's causing the error? Is the difference really the cause of EXC_BAD_ACCESS? If so, by what mechanics?

推荐答案

这实际上很烦人,但对于我们这些在 x86 之前的世界中购买的人来说并不意外:-)

That's actually very annoying, but not so unexpected for those of us bought up in a pre-x86 world :-)

想到的唯一原因(这是纯粹推测)是编译器正在修复"您的代码以确保数据类型正确对齐,但 sizeof/alignof 不匹配导致问题.我似乎记得 ARM6 架构放宽了某些数据类型的一些规则,但从未仔细研究过,因为决定使用不同的 CPU.

The only reason that comes to mind (and this is pure speculation) is that the compiler is "fixing" your code to ensure that the data types are aligned correctly but the sizeof/alignof mismatches are causing problems. I seem to recall that ARM6 architecture relaxed some of the rules for some data types but never got a good look at it because the decision was made to go with a different CPU.

(更新:这实际上是由寄存器设置(因此可能是软件)控制的,所以我想即使是现代 CPU 仍然会抱怨不对齐.

我要做的第一件事是查看生成的程序集,看看编译器是否正在填充您的 short 以对齐下一个(实际)数据类型(这将令人印象深刻)或(更有可能)预在写入之前填充实际的数据类型.

The first thing I would do would be to have a look at the generated assembly to see if the compiler is padding your short to align the next (actual) data type (that would be impressive) or (more likely) pre-padding the actual data type before writing it.

其次,找出我认为是 iPhone4 中使用的核心的 Cortex A8 的实际对齐要求.

Secondly, find out what the actual alignment requirement are for the Cortex A8 which I think is the core used in the IPhone4.

两种可能的解决方案:

1/您可能必须将每种类型转换为 char 数组并一次传输一个字符 - 这有望避免对齐问题,但可能会影响性能.使用 memcpy 可能是最好的,因为它无疑会被编码以利用底层 CPU(例如在可能的情况下传输四字节块,并在开始和结束时使用一字节块).

1/ You may have to cast each type into a char array and transfer the characters one at a time - this should hopefully avoid the alignment issues but may have a performance impact. Use of memcpy would probably be best since it will no doubt be coded to take advantage of the underlying CPU already (such as transferring four-byte chunks where possible with one-byte chunks at the start and end).

2/对于那些不想立即放在 short 之后的数据类型,在 short 之后添加足够的填充以确保它们正确对齐.例如,类似于:

2/ For those data types that don't want to be put immediately after a short, add enough padding after that short to ensure they align correctly. For example, something like:

tmp += sizeof(unsigned short);
tmp = (tmp + sizeof(T)) % alignof(T);

在尝试存储值之前,应该将 tmp 推进到下一个正确对齐的位置.

which should advance tmp to the next properly aligned location before attempting to store the value.

稍后您需要做同样的读取(我假设short 表示正在存储的数据,因此您可以判断它是什么数据类型).

You'll need to do the same reading it back later (I'm assuming the short is indicative of the data being stored so you can tell what data type it is).

为了完整起见,将 OP 的最终解决方案放在答案中(这样人们就不必检查评论):

首先,汇编(在Xcode上,Run menu > Debugger Display > Source and Disassembly)显示处理8字节数据时使用了STMIA指令(即,long long),而不是STR 指令.

First, the assembly (on Xcode, Run menu > Debugger Display > Source and Disassembly) shows that the STMIA instruction is used when handling 8 bytes of data (i.e., long long), instead of the STR instruction.

接下来,《ARM架构参考手册ARMv7-A》(对应Cortex A8的架构)的A3.2.1 Unaligned data access"部分指出STMIA不支持unaligned data access,而STR 可以(取决于某些注册表设置).

Next, section "A3.2.1 Unaligned data access" of the "ARM Architecture Reference Manual ARMv7-A" (the architecture corresponding to Cortex A8) states that STMIA does not support unaligned data access while STR does (depending on certain registry settings).

所以,问题是long long的大小和错位.

So, the problem was the size of long long and misalignment.

至于解决方案,一次一个字符是有效的,作为初学者.

As for the solution, one-char-at-a-time is working, as a starter.

这篇关于为什么这个 EXC_BAD_ACCESS 发生在 long long 而不是 int?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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