2011-10-11 17 views
8

Znalazłem nietypowy przerywacz z NSCoder podczas korzystania z kompilatora Apple LLVM 3.0 i skompilowany z opcją -O3. To tylko zawiesza się na urządzeniach. Przetestowałem iPhone'a 4 z systemem iOS 5, iPada 2 z systemem iOS 5 i iPadem 1 z systemem iOS 4. Wszystkie awarie są identyczne. Oto odpowiedni fragment kodu:Awaria na urządzeniu z systemem iOS podczas usuwania odwołania do wskaźnika zwróconego przez dekodowanie identyfikatora NSCoderBodesForKey

-(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; 
} 

A oto co BBPointI32 jest:

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

EXC_BAD_ACCESS się dzieje, gdy data jest dereferencjonowane. Jest to , a nie problem z pustym wskaźnikiem. Jeśli dołączę GDB, widzę, że długość wynosi 8, sizeof (BBPointI) to także 8, a dane są poprawne.

Jeśli patrzę na demontażu, katastrofa dzieje się na:

ldrd r2, r3, [r0] 

który wygląda dobrze. r0 zawiera 0xb546e, który jest adresem data. Kiedy sprawdzam tę pamięć, widzę, że zawiera ona dane, których się spodziewam. Dla każdego, kto jest zainteresowany, r2 zawiera 72 (nie jestem pewien co to jest), a r3 zawiera 8 (prawdopodobnie wartość length).

Czy ktoś może rzucić trochę światła na tę kwestię?

Odpowiedz

10

ldrd potrzebuje adresu wyrównanego do 8 bajtów. Identyfikator danych * (BBPointI32 *) nie jest bezpieczny, ponieważ dane nie są wyrównane do 8 bajtów. Użyj memcpy, aby zamiast tego uzyskać bajty w strukturze.

+0

Dzięki, to jest na miejscu. Problem * był * związany z decyzją kompilatora o użyciu 'ldrd'. Kiedy przesyłam 'dane' do' BBPointI32 *', zakładałem, że oznaczało to, że wskaźnik był prawidłowo wyrównany, ale tak nie było. Tak więc zamiast: m_value = * (BBPointI32 *) dane; Potrzebowałem użyć: memcpy (& m_value, dane, długość); –

+0

@ robothythmist .. nie tylko przezabawny twitterer :) – ohhorob

+0

Mam coś bardzo podobnego. Zdarza się na iPadzie 3 i _nie_ na iPadzie 2! –

4

Czy używasz ARC? Jeśli tak, to uważam, że problem polega na tym, że kompilator może zwolnić decoder po wywołaniu decodeBytesForKey: (tym samym zwalniając bufor, do którego zwracana jest wartość zwracana).

Jest to ten sam interior pointer issue garbage collection has. Możesz dekoderować swój dekoder, aby przedłużyć jego żywotność, lub po prostu dodać później metodę, aby utrzymać ją przy życiu do tego momentu.

Podejrzewam może również być w stanie rozwiązać ten problem poprzez opisywanie decoder z __attribute__((objc_precise_lifetime)), ale moje rozumienie that attribute jest nieco zamglony.

3

Twój przykład pozostawia wiele zmiennych do przesłuchania przez potencjalnego pomocnika. Na przykład: co zrobić, jeśli jest coś modnego w tym unarchiver? Czy pamięć jest zarządzana poprawnie?

Udało mi się odtworzyć awarię, którą widzisz, i można to potwierdzić tylko wtedy, gdy włączona jest opcja -O3, a nie na przykład Brak jest wybrane do optymalizacji. Oto redukcja upadającego kodu, który eliminuje zewnętrzne zmienne, takie jak zarządzanie pamięcią koderów itp. Zauważ, że poniższy kod celowo zachowuje wszystkie obiekty, aby wyeliminować możliwość, że katastrofa jest związana z przypadkowym nadmiernym zwolnieniem lub z bocznym wpływ stosowania ARC, jak sugeruje Andy inną odpowiedź.

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]; 
} 

myślę, że to daje bardziej zwięzły opis problemu, że ktoś, kto chce pomóc może wykorzystać jako podstawę do nurkowania w moim przeczucie dotąd to jest błąd optymalizacji w LLVM 3.0, ale może ktoś inny będzie miał lepszą teorię na temat tego, co się dzieje.

Punkt, którego nie wspomniałeś w swoim pytaniu, ale który dostrzegam w zawieszeniu na moim urządzeniu, jest niepowodzeniem, któremu towarzyszy wspomnienie o błędzie EXC_ARM_DA_ALIGN jako przyczynie wyjątku złego dostępu.I Googled blogu, który wydaje się nawiązywać do tych samych objawów i prawdopodobnie powodem do upaść jak widzisz tutaj:

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

Rzeczywiście, zmieniając linię powyżej

decodedStruct = *(BBPointI32*)data; 

do

memcpy(&decodedStruct, data, sizeof(decodedStruct)); 

Zachowanie awaryjne wydaje się być złagodzone, a kod zachowuje się zgodnie z oczekiwaniami.

+0

Nie zauważyłem EXC_ARM_DA_ALIGN na moim końcu, ale tak, to jest również zmiana, którą zrobiłem. Dzięki za pomoc. –

1

Mam do tego wątku, googling "EXC_ARM_DA_ALIGN" i "EXC_BAD_ACCESS". Żadna z pozostałych odpowiedzi nie pomogła mi, ponieważ ten błąd pojawił się z powodu czegoś stosunkowo prostego. Napisałem:

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

tj. Odstawiłem znak @ przed literałem ciągu. Ponowne umieszczenie go w rozwiązaniu problemu.

+0

lol - Właśnie spędziłem 2 godziny próbując go wyśledzić. Dziękujemy za opublikowanie rozwiązania. – wufoo

Powiązane problemy