向iOS进军(2): 常见错误处理

1. EXC_BAD_ACCESS code=EXC_ARM_ALIGN

// Begin---参考:http://course.gdou.com/blog/Blog.pzs/archive/2012/9/16/11047.html

在编写FLAC/APE 无损音乐播放器应用时,使用了开源的 SFBAUdioEngine,程序在模拟器上可以播放音乐,但在真机上程序崩溃,错误发生在:

OSAtomicAdd64Barrier(framesDecoded, &mFramesDecoded);

出错信息:

EXC_BAD_ACCESS code=EXC_ARM_ALIGN

调用方法的参数定义:

int64_t mFramesDecoded;

int64_t mFramesRendered;

int64_t mFramesRenderedLastPass;

错误与 armv7 数据内存对齐有关,有关  EXC_ARM_ALIGN 的信息见附录,以及:https://devforums.apple.com/message/209124。

解决办法:

When I ran the app on my device in debug mode it crashed with EXC_BAD_ACCESS. Looking at the crash log on my device: EXC_ARM_DA_ALIGN got me thinking its memory alignment issue and it was at this line -> OSAtomicAdd64Barrier(framesDecoded, &mFramesDecoded); // mFramesDecoded is misaligned.

I had declared the alignment explicitly. In AudioPlayer.h:

int64_t mFramesDecoded __attribute__ ((aligned (8)));;
 int64_t mFramesRendered __attribute__((aligned (8)));
 int64_t mFramesRenderedLastPass __attribute__((aligned (8)));

And in DecoderStateData.h:

SInt64 mTimeStamp __attribute__((aligned (8)));
 SInt64 mTotalFrames __attribute__((aligned (8)));
 volatile SInt64 mFramesRendered __attribute__((aligned (8)));
 SInt64 mFrameToSeek __attribute__((aligned (8)));

修改后可以正常播放音乐。

附录:

Memory Alignment Issues on ARM Processors

Introduction

Memory accesses can be either aligned or unaligned. Aligned memory accesses occur when data is located on its natural size boundary. If the size of the data type is 4 bytes, for example, then it falls on its natural size boundary if it is located at a memory address that is evenly divisible by 4. Unaligned memory accesses occur in all other cases (in the example above, whenever the memory address is not divisible by 4). 

ARM processors are designed to efficiently access aligned data. Attempting to access unaligned data on an ARM processor will result in either the incorrect data or significant performance penalties (these different symptoms will be discussed shortly). This contrasts with most CISC type processors (i.e. x86) in which access to 'unaligned data' is harmless. 

This document will discuss some of the more common ways that an application might perform an unaligned memory access and provide some recommended solutions to avoid these problems.

Symptoms

The problem described above applies to all ARM architectures. However, depending on the availability of an MMU and operating system support, applications may see different behavior across different platforms. By default, unaligned memory accesses will not be trapped, and will result in the incorrect data. On platforms with an enabled MMU, however, the OS may trap the unaligned access and correct it at runtime. The result will be the correct data, but at a cost of 10-20 CPU cycles.

Common Causes

Type Casting
The problem described above applies to all ARM architectures. However, depending on the availability of an MMU and operating system support, applications may see different behavior across different platforms. By default, unaligned memory accesses will not be trapped, and will result in the incorrect data. On platforms with an enabled MMU, however, the OS may trap the unaligned access and correct it at runtime. The result will be the correct data, but at a cost of 10-20 CPU cycles.

Code:

void my_func(char *a) {
  int *b = (int *)a;
  DBGPRINTF("%d", *b);
}

This simple example may result in an unaligned memory access, since we can not guarantee the alignment of char *a is on a 4-byte boundary. This type of cast should be avoided whenever possible.
 

Working with Data Buffers
The most frequent cause of unaligned memory access stems from incorrect handling of data buffers. These data buffers might contain anything ? data read from the USB port, over the network, or from a file. It is common for this data to be packed, meaning there is no padding inserted to ensure that data within the buffer lies on its natural size boundary. In this example, we will consider the case of loading a Windows BMP from a file and parsing the header. 

A Windows BMP file consists of a header followed by the pixel data. The header is made up of two structures:

Code:

typedef PACKED struct {
   unsigned short int type;         /* Magic identifier            */
   unsigned int size;               /* File size in bytes          */
   unsigned short int reserved1, reserved2;
   unsigned int offset;             /* Offset to image data, bytes */
} HEADER;

typedef PACKED struct {
   unsigned int size;               /* Header size in bytes      */
   int width,height;                /* Width and height of image */
   unsigned short int planes;       /* Number of colour planes   */
   unsigned short int bits;         /* Bits per pixel            */
   unsigned int compression;        /* Compression type          */
   unsigned int imagesize;          /* Image size in bytes       */
   int xresolution,yresolution;     /* Pixels per meter          */
   unsigned int ncolours;           /* Number of colours         */
   unsigned int importantcolours;   /* Important colours         */
} INFOHEADER;


Note that the sizes of the HEADER and INFOHEADER structs are 14 and 40 bytes, respectively. 

Lets assume that we want to determine the width and height of the image at runtime. The code to access this data might look like this:

Code:

#define INFOHEADER_OFFSET (sizeof(HEADER))
#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))
#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))

int imageWidth, imageHeight;
void * fileBuf;

pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);

if (pMe->mFile) {
   IFILE_GetInfo(pMe->mFile, &fileInfo);
   fileBuf = MALLOC(fileInfo.dwSize);

   if (fileBuf) {
      result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);

      if (result == fileInfo.dwSize) {
         imageWidth = *((uint32*)(((byte*)fileBuf) + WIDTH_OFFSET));
         imageHeight = *((uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET));
      }
   }
}


Note the offsets of the width and height. Because they fall on a half-word boundary, access to these values in the manner above will result in an unaligned memory access. Some of the recommended ways to avoid this problem are outlined below.

Recommended Solutions


Using MEMCPY

Our first option is to simply perform a MEMCPY of the data from the buffer to our local variable:

Code:

if (result == fileInfo.dwSize) {
   MEMCPY(&imageWidth,
          (((byte*)fileBuf)+WIDTH_OFFSET),
          sizeof(uint32));

   MEMCPY(&imageHeight,
          (((byte*)fileBuf)+HEIGHT_OFFSET), 
          sizeof(uint32));
}



The result is that the memory is copied byte-by-byte, avoiding any questions of alignment. 
 


Using the PACKED Compiler Directive

Alternatively, we can use the PACKED compiler directive to allow use of pointers directly to the data we want, while forcing the compiler to handle the alignment issues. In the BREW environment, PACKED is defined as follows:

Code:

#ifdef __ARMCC_VERSION
  #define PACKED __packed
#else
  #define PACKED
#endif



By designating a pointer as PACKED, the ARM compiler will always generate the appropriate instructions to access the memory correctly, regardless of alignment. A modified version of the example above, using PACKED pointers, is given below: 
 

Code:

#define INFOHEADER_OFFSET (sizeof(HEADER))
#define WIDTH_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, width))
#define HEIGHT_OFFSET (INFOHEADER_OFFSET + offsetof(INFOHEADER, height))

PACKED uint32 * pImageWidth;
PACKED uint32 * pImageHeight;
uint32 imageWidth, imageHeight;
void * fileBuf;

pMe->mFile = IFILEMGR_OpenFile(pMe->mFileMgr, "test.bmp", _OFM_READ);

if (pMe->mFile) {
   IFILE_GetInfo(pMe->mFile, &fileInfo);
   fileBuf = MALLOC(fileInfo.dwSize);

   if (fileBuf) {
      result = IFILE_Read(pMe->mFile, fileBuf, fileInfo.dwSize);

      if (result == fileInfo.dwSize) {
         pImageWidth = (uint32*)(((byte*)fileBuf) + WIDTH_OFFSET);
         pImageHeight = (uint32*)(((byte*)fileBuf) + HEIGHT_OFFSET);
         imageWidth = *pImageWidth;
         imageHeight = *pImageHeight;
      }
   }
}



Defining Well-Aligned Data Structures

While programmers will typically have no control over standardized data formats, such as the BMP header used in the example above, when defining your own data structures you should be sure to lay out the data in a well-aligned way. The following basic example demonstrates this principle:

Code:

#ifdef __ARMCC_VERSION
typedef PACKED struct {
   short a;		// offsetof(a) = 0
   int   b;		// offsetof(b) = 2 ? misalignment problem!
   short c;		// offsetof(c) = 6
} BAD_STRUCT;
 
typedef struct {
   int   b;		// offsetof(b) = 0 ? no problem!
   short a;		// offsetof(a) = 4
   short c;		// offsetof(c) = 6
} GOOD_STRUCT; 



Simply by rearranging the order in which we declare the struct members, we can resolve some of the alignment issues. Also note that if BAD_STRUCT is not declared as PACKED, the compiler will typically insert padding such that each field is well-aligned. This, however, is usually undesirable as it wastes memory and can almost always be avoided simply by declaring fields in order of decreasing size. 

 // Begin---参考:http://course.gdou.com/blog/Blog.pzs/archive/2012/9/16/11047.html

总结:

错误原因:1.Type Casting     2.Working with Data Buffers

解决办法:1.Using MEMCPY    2. Using the PACKED Compiler Directive    3.Defining Well-Aligned Data Structures

我的错误是由于Type Casting造成的,所以我使用内存拷贝方法解决了。。。


 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值