2011-10-11 1 views
8

Apple LLVM Compiler 3.0을 사용하고 -O3으로 컴파일 할 때 NSCoder와 함께 특이한 크래셔를 발견했습니다. 그것은 장치에서만 충돌합니다. iOS 5를 실행하는 iPhone 4, iOS 5를 실행하는 iPad 2 및 iOS 4를 실행하는 iPad 1을 테스트했습니다. 모든 충돌이 동일합니다.NSCoder의 decodeBytesForKey에 의해 반환 된 포인터를 참조 해제 할 때 iOS 장치에서 충돌이 발생했습니다.

-(id)initWithCoder:(NSCoder*)decoder 
{ 
    if (![super init]) 
    { 
     return nil; 
    } 

    NSUInteger length = 0; 

    uint8_t* data = (uint8_t*)[decoder decodeBytesForKey:BBKey returnedLength:&length]; 

    m_value = *(BBPointI32*)data; 

    return self; 
} 

을 그리고 여기에 BBPointI32이 작업은 다음과 같습니다 : 여기에 코드의 관련 섹션입니다 data가 역 참조 될 때

typedef struct 
{ 
    NSInteger x; 
    NSInteger y; 
} 
BBPointI32; 

EXC_BAD_ACCESS 발생합니다. 이것은 이 아니며 null 포인터 문제입니다. GDB를 첨부하면 길이가 8이고 sizeof (BBPointI)가 8이며 데이터가 정확한지 확인할 수 있습니다.

내가 해체 보면

, 충돌은에 무슨 일이 일어나고 : 잘 보이는

ldrd r2, r3, [r0] 

합니다. r0은 0xb546e를 포함하며, 주소는 data입니다. 내가 그 기억을 조사 할 때, 나는 그것이 내가 기대하는 데이터를 포함하고 있음을 볼 수있다. 관심있는 사람에게는 r2에 72 개가 포함되어 있는지 (그 내용이 확실하지 않음) r3에 8 개가 포함되어 있습니다 (대부분 length의 값).

누구든지이 문제에 관해 밝힐 수 있습니까?

답변

10

ldrd는 8 바이트 단위로 정렬되어야합니다. * (BBPointI32 *) 데이터 관용구는 데이터가 8 바이트로 정렬되지 않으므로 안전하지 않습니다. 구조체에 바이트를 가져 오려면 memcpy를 사용하십시오.

+0

고마워, 그 자리에있어. 이 문제는 컴파일러가'ldrd'를 사용하기로 결정한 것과 관련이 있습니다. ''BBPointI32 * '에'데이터'를 전송할 때 포인터가 제대로 정렬되었다는 것을 의미한다고 가정했으나 그렇지 않습니다. 그래서, 대신 : m_value = * (BBPointI32 *) data; 다음과 같이 사용해야합니다. memcpy (& m_value, data, length); –

+0

@biorhythmist .. 그냥 들뜬 twitterer :) – ohhorob

+0

나는 아주 비슷한 것을 경험하고 있습니다. iPad 3에서 발생하고 _not_ iPad 2에서 발생합니다! –

4

ARC를 사용하고 있습니까? 그렇다면 컴파일러가 호출 후에 decoder을 해제 할 수 있다는 것이 문제라고 생각합니다. 따라서 반환 값이 가리키는 버퍼가 해제됩니다.

동일 함 interior pointer issue garbage collection has입니다. 디코더의 수명을 연장하기 위해 CFRetain/CFRelease 디코더를 사용할 수 있으며, 그렇지 않은 경우 나중에 [decoder self]을 메서드에 추가하여 그 시점까지 계속 유지할 수 있습니다.

decoder__attribute__((objc_precise_lifetime))이라는 주석을 달아이 문제를 해결할 수도 있지만, that attribute에 대한 이해는 다소 흐립니다.

3

예를 들어 잠재적 인 도우미가 물어볼 변수가 많습니다. 예를 들어,이 파삭 파삭 한 곳에서 뭔가 펑키 한 것이 있다면? 메모리가 올바르게 관리되고 있습니까?

내가보고있는 크래시를 재생할 수 있었으며 -O3이 사용 설정된 경우에만이를 확인할 수 있습니다. 최적화를 위해 없음이 선택됩니다. 코더의 메모리 관리와 같은 외부 변수를 제거하는 크래시 코드의 감소는 다음과 같습니다. 아래의 코드는 모든 오브젝트를 의도적으로 유지하므로 크래시가 의도하지 않은 과도한 릴리스 또는 부작용과 관련된 가능성을 제거합니다. 다른 답변에서 앤디에 의해 제안, ARC를 사용 효과 :.

typedef struct 
{ 
    NSInteger x; 
    NSInteger y; 
} 
BBPointI32; 

- (void) testDecoding 
{ 
    NSString* myKey = @"Testing"; 

    // First get an coder with bytes in it 
    NSMutableData* myData = [[NSMutableData data] retain]; 
    NSKeyedArchiver* myCoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:myData]; 

    BBPointI32 encodedStruct = {1234, 5678}; 
    [myCoder encodeBytes:(const uint8_t *)&encodedStruct length:sizeof(encodedStruct) forKey:myKey]; 
    [myCoder finishEncoding]; 

    // Now decode it 
    BBPointI32 decodedStruct; 
    NSUInteger decodedLength = 0; 
    NSKeyedUnarchiver* myDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:myData]; 
    uint8_t* data = (uint8_t*)[myDecoder decodeBytesForKey:myKey returnedLength:&decodedLength]; 
    decodedStruct = *(BBPointI32*)data; 
    NSLog(@"Got decoded struct with x = %ld, y = %ld, length = %lu", decodedStruct.x, decodedStruct.y, decodedLength); 
} 

- (void)applicationDidFinishLaunching:(UIApplication *)application {  
    NSLog(@"Testing decoding"); 
    [self testDecoding]; 
} 
내가이 도움을 원하는 사람은 다이빙의 기초로 사용할 수있는 문제에 대한보다 간결한 설명을 제공합니다 생각 내 직감까지 이것이 LLVM 3.0의 최적화 버그일까요?하지만 다른 누군가가 무슨 일이 일어나고 있는지 더 좋은 이론을 가지고있을 것입니다.

귀하의 질문에 언급하지 않았지만 내 장치에서 충돌로 알아 차린 점은 잘못된 액세스 예외의 원인으로 EXC_ARM_DA_ALIGN 오류에 대한 언급이 수반된다는 것입니다.

http://www.galloway.me.uk/2010/10/arm-hacking-exc_arm_da_align-exception/

사실,

decodedStruct = *(BBPointI32*)data; 

위의 라인을 변경하여

에 : 여기보고있는 같은 증상을 암시하고 아마 당신 같은 충돌에 대한 원인 것으로 보인다 블로그 포스트를 봤
memcpy(&decodedStruct, data, sizeof(decodedStruct)); 

충돌하는 동작이 완화되어 코드가 예상대로 동작합니다.

+0

내 끝 부분에 EXC_ARM_DA_ALIGN을 알지 못했지만, 그래도 그 변경 사항이 있습니다. 당신의 도움을 주셔서 감사합니다. –

1

"EXC_ARM_DA_ALIGN"및 "EXC_BAD_ACCESS"로 인터넷에 접속했습니다. 이 오류는 상대적으로 간단한 무언가 때문에 잘랐다. 나는 다음과 같이 썼다 :

theArray = [[NSArray alloc] initWithObjects:@"first", @"second", @"third", 
         @"fourth", @"fifth", "sixth", nil]; 

즉 나는 문자열 리터럴 앞에 @를 붙였다. 그것을 다시 넣으면 오류가 해결되었습니다.

+0

lol - 방금이 시간을 추적하려고 2 시간을 보냈습니다. 솔루션을 게시 주셔서 감사합니다. – wufoo

관련 문제