2012-12-31 18 views
5

Sto cercando di imparare smali e ho alcune domande che non sono riuscito a trovare su google.In che modo DalvikVM gestisce lo switch e prova il codice smali

1) Ho creato un semplice banco di prova per spiegare meglio me stesso

const-string v1, "Start" 
:try_start_0 
const-string v1, "Try Block" 
invoke-static {v1}, Lcom/example/test/Main;->print(Ljava/lang/String;)V 
:try_end_0 
.catchall {:try_start_0 .. :try_end_0} :catchall_0 

La dichiarazione .catch: fa i due argomenti medie catturano da quell'etichetta a quell'etichetta e lo prende (il codice tra le due etichette) o significa iniziare ad eseguire la prova da: try_start_0 fino a raggiungere: try_end_0 (consente a un goto di saltare per eseguire codice non all'interno delle due etichette)?

Le etichette provano sempre nel formato try_start_% d o possono essere etichette?

2) Un altro caso

packed-switch v0, :pswitch_data_0 

const-string v1, "Default Case" 

invoke-static {v1}, Lcom/example/test/Main;->print(Ljava/lang/String;)V 

:goto_0 

const-string v1, "The End" 

invoke-static {v1}, Lcom/example/test/Main;->print(Ljava/lang/String;)V 

return-void 

:pswitch_0 
const-string v1, "Case 1" 

invoke-static {v1}, Lcom/example/test/Main;->print(Ljava/lang/String;)V 

goto :goto_0 

:pswitch_data_0 
.packed-switch 0x1 
:pswitch_0 
.end packed-switch 

L'istruzione switch: C'è bisogno che le istruzioni switch si trovano tra i dati di interruttore e la chiamata switch? e anche di nuovo la denominazione delle etichette fissate o solo quella per comodità?

3) Se le etichette possono essere diverse, Baksmali potrebbe mai produrre codice smali con diverse etichette?

4) Quali sono le righe opzionali che non vengono sempre visualizzate quando si decompila un dex?

So che .parameter e .line sono facoltativi, ma quali sono tutti quelli che potrebbero non esserci?

Grazie in anticipo.

risposta

4

1)

Il primo due etichette (try_start_0 e try_end_0 nel tuo esempio) definiscono l'intervallo di codice che le copertine blocco try. Se si verifica un'eccezione all'interno del codice coperto, l'esecuzione salta immediatamente alla terza etichetta (catchall_0). Il nome dell'etichetta non è importante, può essere un qualsiasi identificatore valido.

C'è anche la direttiva .catch, con è la stessa cosa, tranne che gestisce solo un tipo specifico di eccezione (simile all'istruzione catch di java).

Un blocco di codice può essere coperto da più istruzioni catch e al massimo 1 catch all statement. L'ubicazione dell'istruzione .catch non è importante, tuttavia l'ordine relativo delle dichiarazioni catch che coprono lo stesso codice è importato. Ad esempio, se disponi di

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :handler1 
.catch Ljava/lang/RuntimeException; {:try_start_0 .. :try_end_0} :handler2 

La seconda frase di cattura non verrà mai utilizzata. Se viene lanciata una RuntimeException nel codice coperto, verrà utilizzato sempre il primo catch, poiché RuntimeException è un'eccezione.

Tuttavia, se fossero nell'ordine opposto, funzionerebbe come previsto: il gestore RuntimeException viene utilizzato per RuntimeExceptions e il gestore di eccezioni viene utilizzato per qualsiasi altro tipo di eccezione.

Infine, a differenza di java, l'intervallo di codice nelle istruzioni .catch non deve necessariamente essere nidificato. Ad esempio, è perfettamente legale per avere qualcosa di simile

:a 
const-string v1, "Start" 
:b 
const-string v1, "Try Block" 
:c 
invoke-static {v1}, Lcom/example/test/Main;->print(Ljava/lang/String;)V 
:d 
.catch Ljava/lang/RuntimeException; {:a .. :c} :d 
.catch Ljava/lang/Exception; {:b .. :d} :d 

Si può anche avere alcune costruzioni piuttosto strani, come questo.

.method public static main([Ljava/lang/String;)V 
    .registers 3 

    :second_handler 
    :first_try_start 
     new-instance v0, Ljava/lang/RuntimeException; 
     invoke-direct {v0}, Ljava/lang/RuntimeException;-><init>()V 
     throw v0 
    :first_try_end 
    .catch Ljava/lang/Exception; {:first_try_start .. :first_try_end} :first_handler 
    :first_handler 
    :second_try_start 
     new-instance v0, Ljava/lang/RuntimeException; 
     invoke-direct {v0}, Ljava/lang/RuntimeException;-><init>()V 
     throw v0 
    :second_try_end 
    .catch Ljava/lang/Exception; {:second_try_start .. :second_try_end} :second_handler 
.end method 

Nessuno dei suddetti esempi sarebbero mai generato dal codice Java compilato, ma la stessa bytecode permette.

2) Le istruzioni di commutazione possono essere ovunque in relazione all'istruzione switch o ai dati di commutazione. Anche i nomi delle etichette qui sono arbitrari.

3) Baksmali può generare etichette in uno dei 2 modi. Il modo predefinito è utilizzare il "tipo" generale dell'etichetta e aggiungere l'indirizzo bytecode dell'etichetta. Se si specifica l'opzione -s/- sequential-labels, anziché utilizzare l'indirizzo bytecode, mantiene un contatore per ogni tipo di etichetta e lo incrementa ogni volta che genera un'etichetta di quel tipo.

4) Generalmente tutto ciò che fa parte delle informazioni di debug. .parameter, .line, .prologue, .epilogue, .source, .local, .restart local, .end local ... Penso che lo copra.

+0

Grazie. Questo è esattamente quello che volevo sapere. Mi piace particolarmente la tua seconda costruzione "strana" :). So che il codice java compilato non sarà in grado di fornirlo, ma un programma di ottimizzazione sarebbe in grado di ottimizzare il codice in uno stato che non è realizzabile con il normale codice java (un po 'come goto C/C++ che consente "strano" flusso di controllo)? – Xonar

+0

Un ottimizzatore, probabilmente no. Tuttavia, potrebbe essere più probabile da un offuscatore – JesusFreke