2011-10-14 11 views
5

Recentemente ho riscontrato uno strano comportamento di ARM Cortex-A8 durante la sua programmazione in Assembly. Ogni volta che MOV niente nella R4, il mio programma si blocca (dump di stack sotto)Impossibile scrivere nel registro ARM R4: funzionalità o bug?

10-14 09:48:43.117: INFO/DEBUG(3048): Build fingerprint: 'google/soju/crespo:2.3.6/GRK39F/189904:user/release-keys' 
10-14 09:48:43.121: INFO/DEBUG(3048): pid: 7082, tid: 7082 >>> neontests <<< 
10-14 09:48:43.121: INFO/DEBUG(3048): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000001 
10-14 09:48:43.125: INFO/DEBUG(3048): r0 00000001 r1 afa025b6 r2 00000000 r3 bec77051 
10-14 09:48:43.128: INFO/DEBUG(3048): r4 00000001 r5 bec7704c r6 00000001 r7 00000004 
10-14 09:48:43.128: INFO/DEBUG(3048): r8 00000005 r9 00000000 10 4214cca4 fp 800a5368 
10-14 09:48:43.128: INFO/DEBUG(3048): ip afa03110 sp bec77010 lr afa0133b pc afd37b42 cpsr 60000030 
10-14 09:48:43.132: INFO/DEBUG(3048): d0 0000000200000053 d1 0000000400000074 
10-14 09:48:43.132: INFO/DEBUG(3048): d2 000000060000006f d3 0000000800000070 
10-14 09:48:43.132: INFO/DEBUG(3048): d4 006f0065006e002e d5 007300650074006e 
10-14 09:48:43.136: INFO/DEBUG(3048): d6 0000000c00000005 d7 0000002000000015 
10-14 09:48:43.136: INFO/DEBUG(3048): d8 0000000c00000005 d9 0000002000000015 
10-14 09:48:43.140: INFO/DEBUG(3048): d10 0000000000000000 d11 0000000000000000 
10-14 09:48:43.140: INFO/DEBUG(3048): d12 0000000000000000 d13 0000000000000000 
10-14 09:48:43.140: INFO/DEBUG(3048): d14 0000000000000000 d15 0000000000000000 
10-14 09:48:43.144: INFO/DEBUG(3048): d16 800220e8401644a8 d17 bff0000000000000 
10-14 09:48:43.144: INFO/DEBUG(3048): d18 3ff0000000000000 d19 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d20 0000000000000000 d21 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d22 3ff0000000000000 d23 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d24 3ff0000000000000 d25 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d26 0000000000000000 d27 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d28 0000000000000000 d29 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): d30 0000000000000000 d31 0000000000000000 
10-14 09:48:43.148: INFO/DEBUG(3048): scr 20000012 
10-14 09:48:43.195: INFO/DEBUG(3048):   #00 pc 00037b42 /system/lib/libc.so 
10-14 09:48:43.195: INFO/DEBUG(3048):   #01 pc 00001338 /system/lib/liblog.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #02 pc 00001482 /system/lib/liblog.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #03 pc 00000c54 /data/data/neontests/lib/libneon_tests.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #04 pc 00017e34 /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #05 pc 0004968c /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #06 pc 0004ee62 /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #07 pc 0001d034 /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #08 pc 000220e4 /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #09 pc 00020fdc /system/lib/libdvm.so 
10-14 09:48:43.199: INFO/DEBUG(3048):   #10 pc 0005fdde /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #11 pc 00067b52 /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #12 pc 0001d034 /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #13 pc 000220e4 /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #14 pc 00020fdc /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #15 pc 0005fc40 /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #16 pc 0004c126 /system/lib/libdvm.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #17 pc 00032572 /system/lib/libandroid_runtime.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #18 pc 0003341e /system/lib/libandroid_runtime.so 
10-14 09:48:43.203: INFO/DEBUG(3048):   #19 pc 00008cca /system/bin/app_process 
10-14 09:48:43.207: INFO/DEBUG(3048):   #20 pc 00014b52 /system/lib/libc.so 
10-14 09:48:43.207: INFO/DEBUG(3048): code around pc: 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b20 18801889 c003f810 c003f801 d2f93b01 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b30 bf00bdf0 2200b510 3201e003 4618b90b 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b40 5c83e004 42a35c8c 1b18d0f7 bf00bd10 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b50 b152b530 5cc42300 42ac5ccd 1b60d001 
10-14 09:48:43.207: INFO/DEBUG(3048): afd37b60 b114e004 429a3301 2000d1f5 bf00bd30 
10-14 09:48:43.207: INFO/DEBUG(3048): code around lr: 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01318 fffffff4 00001e20 b088b570 4615460c 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01328 b9099001 447c4c28 46204928 f7ff4479 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01338 2800edc4 4926d02e 22034620 f7ff4479 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01348 b338edc2 46204923 f7ff4479 b308edb6 
10-14 09:48:43.207: INFO/DEBUG(3048): afa01358 46204921 f7ff4479 b1d8edb0 4620491f 
10-14 09:48:43.207: INFO/DEBUG(3048): stack: 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fd0 800a5368 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fd4 afd1c701 /system/lib/libc.so 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fd8 bec771f0 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fdc bec77051 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fe0 0000ce60 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fe4 000003fa 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fe8 ffff0208 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76fec bec7704c 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76ff0 000003ff 
10-14 09:48:43.207: INFO/DEBUG(3048):  bec76ff4 00000000 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec76ff8 00000003 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec76ffc 00000004 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77000 80400d90 /data/data/neontests/lib/libneon_tests.so 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77004 bec7704c 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77008 df002777 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec7700c e3a070ad 
10-14 09:48:43.210: INFO/DEBUG(3048): #00 bec77010 00000001 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77014 afa0133b /system/lib/liblog.so 
10-14 09:48:43.210: INFO/DEBUG(3048): #01 bec77018 80400420 /data/data/neontests/lib/libneon_tests.so 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec7701c 00000004 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77020 bec7701c 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77024 00000001 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77028 80400d90 /data/data/neontests/lib/libneon_tests.so 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec7702c 00000014 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77030 00000000 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77034 00000000 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77038 bec7704c 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec7703c afd4d5c8 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77040 00000001 
10-14 09:48:43.210: INFO/DEBUG(3048):  bec77044 afa01487 /system/lib/liblog.so 

Modifica: dump di stack sopra è il risultato del seguente codice (scuse, Assemblea GNU evidenziazione sembra essere un po 'strano qui) :

.arm 
.global asm_test 

asm_test: 

    mov r0, #4 @make sure r0 is not the same as r4 
    mov r4, #1 @move to r4 something different from r0 

    mov pc, lr @return from function 

sto chiamando da C (nativo) come segue:

#include <jni.h> 
#include <string.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <arm_neon.h> 
#include <android/log.h> 
#include "com_something_neontests_NativeLib.h" 

extern volatile int asm_test(void); 

JNIEXPORT jint JNICALL Java_com_something_neontests_NativeLib_asmTry 
    (JNIEnv * env, jobject obj) 
{ 

    __android_log_print(ANDROID_LOG_INFO, "com.something.neontests", "Start!"); 

    asm_test(); 

    __android_log_print(ANDROID_LOG_INFO, "com.something.neontests", "Done!"); 


    return 0; 
} 

qui ci sono alcune cose che ho notato. Innanzitutto, quando assegno nulla a R4, sia MOV R4, #2 o ADD R4, R0, R1, il risultato fa termina in R4 prima che il programma si arresti, ma lo stesso risultato finisce sempre in R0. Ho anche scoperto che posso fare roba POP dallo stack in R4. Nessun altro registro mostra questo stesso comportamento. Compilare il codice assembly utilizzando Android NDK, che credo utilizzi GCC 4.4.3. L'ho provato su diversi telefoni Android e tutto sembra essere coerente.

So che tutti i registri sono sezionati in modo tale che R0-R3 accetta argomenti, R4-R12 sono registri variabili, quindi ci sono registri speciali e così via. Forse questo comportamento è causato da una sorta di convenzione di chiamata C di cui non ho mai sentito parlare? C'è una spiegazione per questo, è previsto?

Cheers! =)

Aggiornamento:

Come @Graham gentilmente sottolineato, r4 (alternativamente v1) è un registro variabile che deve essere conservato. Tuttavia, nel link fornito nella sua risposta, il braccio documentazione stessa si avvale del registro v1, salvando innanzitutto il suo risultato sullo stack con valore di un altro registro conservato:

STMDB sp!,{v1,lr} 
LDR v1,[a2,#0] 

e poi recuperare i loro valori. Quando compilo questo codice, si blocca allo stesso modo come la mia originale, ma

STMDB sp!,{v1,lr} 
LDR v2,[a2,#0] 

non lo fa (avviso v2 invece di v1).

+0

Puoi mostrare il codice più piccolo possibile che si sta bloccando? 'fault addr 00000001' sembra suggerire che stai caricando dall'indirizzo contenuto in' r4'. Lo stesso risultato mostrato in 'r0' sembra strano. – user786653

+0

@ user786653 Certo, modifica la mia domanda. – Phonon

+0

Ho ancora la sensazione che non venga mostrato tutto il codice rilevante. Hai un esempio autonomo che mostra questo comportamento? – user786653

risposta

8

Quello che stiamo cercando di spiegare è che avete bisogno di fare questo se si desidera utilizzare r4 in una funzione:

.globl asm_test 
asm_test: 
    stmdb r13!,{r4} 
    mov r0, #4 @make sure r0 is not the same as r4 
    mov r4, #1 @move to r4 something different from r0 
    ldmia r13!,{r4} 
    mov pc, lr @return from function 

In caso contrario si lascia una bomba a orologeria che si spegne ad un certo punto lungo la strada. Il compilatore ha assegnato r4 per qualcosa in una funzione di livello superiore, e con le regole nessuno può modificare quel registro in modo che la chiamata di livello superiore non debba proteggere r4, facendo confusione al momento giusto e posizionando il problema, come il problema si comporta dipende dal codice. E spiegherà perché altri registri, in questo caso, non sono sensibili. A volte quando si esegue questa operazione non si blocca effettivamente, a volte forse una stringa viene stampata in modo errato o un ciclo si ripete o esce prima.

Per vedere cosa sta succedendo si prega di smontare la funzione in questione (non il codice sorgente ma uno smontaggio). più le funzioni che lo hanno chiamato e le funzioni che lo hanno chiamato fino a quando r4 compare in una di quelle funzioni circostanti. esaminare per cosa viene usato r4.

È inoltre possibile modificare il comportamento se la funzione di chiamata asm_test() dovesse avere variabili locali utilizzate prima e dopo la chiamata asm_test() in modo che l'ottimizzatore le tenga nei registri, ma anche in modo tale che il ottimizzatore non rimuove il codice tutto insieme:

void fun (void) 
{ 
    int r; 
    r=10; 
    asm_test(); 
    r++; 
} 

l'ottimizzatore eliminerebbe completamente r nel codice qui sopra, ma:

int fun (int a, int b, int c, int d) 
{ 
    int e; 
    e=a+b+c+d; 
    b=asm_test(a+d); 
    e+=b; 
    return(e); 
} 

crea più che sufficiente per forzare il compilatore di costruire uno stack frame.

00000000 <fun>: 
    0: e0811000 add r1, r1, r0 
    4: e92d4010 push {r4, lr} 
    8: e0830000 add r0, r3, r0 
    c: e0814002 add r4, r1, r2 
    10: e0844003 add r4, r4, r3 
    14: ebfffffe bl 0 <asm_test> 
    18: e0840000 add r0, r4, r0 
    1c: e8bd8010 pop {r4, pc} 

r4 è la variabile e in questo caso (circa la chiamata asm_test) e rovinare r4 si cambierà ciò che la funzione di divertimento) restituisce (. Se quel valore non è mai stato usato per la chiamata al divertimento, per esempio la tua modifica di r4 sarebbe passata inosservata.

I compilatori seguono le regole della convenzione di chiamata e si aspettano anche tutti i callees, se si scherza con il fatto che il modo in cui può crash/fallire non va da alcun effetto a molto serio, quindi è necessario conformarsi a tali convenzioni di chiamata in il tuo asm.

+0

Quello che stai dicendo è che si blocca perché ho cambiato il valore di r4 in qualcosa che una funzione di livello superiore non si aspetta e il programma si arresta in modo anomalo quando la mia funzione è tornata. Corretta? – Phonon

+0

Grazie, ora capisco! – Phonon

7

Secondo APCS, R4 è uno dei registri che è necessario conservare. Se hai bisogno di usarlo, allora memorizzalo in pila alla voce, e spegnilo di nuovo all'uscita. Esistono alcuni registri, ad esempio R0-R3, che sono registri scratch; ti è permesso di corromperli all'interno della tua routine senza conservarli.

See the docs per una descrizione di quali registri è necessario conservare e ripristinare prima di tornare dalla propria routine.

v1-v8, [F4-F7]

Questi sono usati come variabili di registro. Devono essere preservati dalle funzioni chiamate.

v1 è il nome alternativo APCS per R4.

+0

È tutto vero, ma il fatto che debba essere conservato non spiegare l'incidente. Ho aggiornato la mia risposta, per favore date un'occhiata. – Phonon