Smali Syntax and Class Structure Reference
Dalvik bytecode executes on a register-based VM. Each virtual registre is 32 bits wide; 64‑bit primitives (long, double) consume a pair of adjacent registers. Smali is the human-readable assembly for Dalvik, while baksmali is its decompiler.
1. Types in Dalvik/Smali
Primitive type descriptors:
| Code | Meaning |
|---|---|
| V | void (return only) |
| Z | boolean |
| B | byte |
| S | short |
| C | char |
| I | int |
| J | long (64‑bit) |
| F | float |
| D | double (64‑bit) |
Reference types and arrays:
- Objects: Lpackage/ClassName; (example: Ljava/lang/String;) → java.lang.String
- One-dimensional arrays: [I, [F, [Ljava/lang/String;, etc.
- Multi-dimensional arrays: add one [ per dimension, e.g., [[I → int[][]
2. Core Syntax
2.1 Expressions
Java:
public void mathOps() {
int x = 2;
double y = 3.25;
double sum = x + y;
double diff = y - x;
double prod = x * y;
double quot = y / x;
int m = 5;
int xorRes = x ^ m;
int pick = (x < y) ? m : x;
}
Smali:
.method public mathOps()V
.locals 16
const/4 v0, 0x2 # x
const-wide v1, 0x400a000000000000L # y = 3.25
# sum = x + y
int-to-double v3, v0
add-double/2addr v3, v1 # v3-v4 hold sum
# diff = y - x
int-to-double v5, v0
sub-double v5, v1, v5 # v5-v6 hold diff
# prod = x * y
int-to-double v7, v0
mul-double/2addr v7, v1 # v7-v8 hold prod
# quot = y / x
int-to-double v9, v0
div-double v9, v1, v9 # v9-v10 hold quot
# xorRes = x ^ m
const/4 v11, 0x5 # m
xor-int v12, v0, v11
# pick = (x < y) ? m : x
int-to-double v5, v0 # reuse v5-v6 for compare lhs
cmpl-double v13, v5, v1 # v13: -1 if x<y, 0 if ==, 1 if >
if-ltz v13, :lt
move v14, v0
goto :done
:lt
move v14, v11
:done
return-void
.end method
Operator families commonly used:
| Java op | Smali (examples) |
|---|---|
| + | add-int, add-float, add-double |
| - | sub-int, sub-float, sub-double |
| * | mul-int, mul-float, mul-double |
| / | div-int, div-float, div-double |
| ^ (bitwise XOR) | xor-int, xor-long |
| ternary a ? b : c | if-… + move/goto |
2.2 Conditional Branching
Java:
public void branchOps() {
int p = 7, q = 4;
int r = -1;
if (p <= q) r = p;
if (p > q) r = q;
if (p == q) r = 0;
if (p != q) r = 1;
if (p >= q) r = 2;
if (p < q) r = 3;
}
Smali:
.method public branchOps()V
.locals 3
const/4 v0, 0x7 # p
const/4 v1, 0x4 # q
const/4 v2, -0x1 # r
if-gt v0, v1, :skip_le
move v2, v0 # r = p when p <= q
:skip_le
if-le v0, v1, :skip_gt
move v2, v1 # r = q when p > q
:skip_gt
if-ne v0, v1, :skip_eq
const/4 v2, 0x0 # r = 0 when p == q
:skip_eq
if-eq v0, v1, :skip_ne
const/4 v2, 0x1 # r = 1 when p != q
:skip_ne
if-lt v0, v1, :skip_ge
const/4 v2, 0x2 # r = 2 when p >= q
:skip_ge
if-ge v0, v1, :end
const/4 v2, 0x3 # r = 3 when p < q
:end
return-void
.end method
Single-register comparisons against zero are also available: if-eqz, if-nez, if-ltz, if-lez, if-gtz, if-gez.
Semantisc overview:
- if-eq vA, vB, :label → jump if vA == vB
- if-ne vA, vB, :label → jump if vA != vB
- if-lt vA, vB, :label → jump if vA < vB
- if-le vA, vB, :label → jump if vA <= vB
- if-gt vA, vB, :label → jump if vA > vB
- if-ge vA, vB, :label → jump if vA >= vB
- Zero variants (…z) compare vA with 0
2.3 Loops
Java:
public void loopsDemo() {
int w = 0; // while
while (w <= 3) {
w++;
}
int acc = 0; // for
for (int i = 1; i < 3; i++) {
acc += i;
}
int z = 0; // do..while
do {
z++;
} while (z <= 3);
}
Smali:
.method public loopsDemo()V
.locals 6
# while (w <= 3) { w++; }
const/4 v0, 0x0 # w
:while_head
const/4 v1, 0x3
if-gt v0, v1, :after_while
add-int/lit8 v0, v0, 0x1
goto :while_head
:after_while
# for (int i = 1; i < 3; i++) { acc += i; }
const/4 v2, 0x1 # i
const/4 v3, 0x0 # acc
:for_check
if-ge v2, v1, :after_for
add-int v3, v3, v2
add-int/lit8 v2, v2, 0x1
goto :for_check
:after_for
# do { z++; } while (z <= 3)
const/4 v4, 0x0 # z
:do_body
add-int/lit8 v4, v4, 0x1
if-le v4, v1, :do_body
return-void
.end method
Key pattern: place a label at the top of the loop body, branch out on the exit condition, and jump back with goto for the next iteration.
2.4 try/catch/final
Java:
public void tryCatchFinally() {
Object o = null;
try {
o.toString(); // will throw if o == null
} catch (Exception ex) {
o = new Object();
} finally {
o = null;
}
}
Smali:
.method public tryCatchFinally()V
.locals 3
const/4 v0, 0x0 # o
:try_start_0
invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
:try_end_0
goto :finally_block
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_ex
:catch_ex
move-exception v2
new-instance v1, Ljava/lang/Object;
invoke-direct {v1}, Ljava/lang/Object;-><init>()V
move-object v0, v1
goto :finally_block
:finally_block
const/4 v0, 0x0 # o = null (finally)
return-void
.end method
To guarantee finally executes for any throwable, add a catchall handler and rethrow after executign the finally block.
.method public tryCatchFinallyStrict()V
.locals 3
const/4 v0, 0x0 # o
:tstart
invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;
:tend
goto :fin
.catch Ljava/lang/Exception; {:tstart .. :tend} :handler
.catchall {:tstart .. :tend} :all
:handler
move-exception v2
new-instance v1, Ljava/lang/Object;
invoke-direct {v1}, Ljava/lang/Object;-><init>()V
move-object v0, v1
goto :fin
:all
move-exception v2
const/4 v0, 0x0 # finally
throw v2
:fin
const/4 v0, 0x0 # finally
return-void
.end method
3. Smali Class File Layout
3.1 Header
.class public Lcom/example/smali/IntroActivity;
.super Landroid/app/Activity;
.source "IntroActivity.java"
- .class: fully-qualified class descriptor
- .super: superclass descriptor
- .source: original source file name
3.2 Constructor
# direct methods
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
p0 always references the current instance (this) in instance methods.
3.3 Virtual Methods
Example: onCreate
# virtual methods
.method protected onCreate(Landroid/os/Bundle;)V
.locals 1
.param p1, "savedInstanceState" # Landroid/os/Bundle;
invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
const v0, 0x7f0a0000 # layout id
invoke-virtual {p0, v0}, Lcom/example/smali/IntroActivity;->setContentView(I)V
return-void
.end method
Notes:
- Method signature uses type descriptors; return type V means void
- Parameters follow the same descriptors; p0 is this, p1..pN are parameters
- .locals declares the number of non-paramter registers v0..vN used within the method
3.4 Common Directives and Markers
- .class: declare class descriptor
- .super: declare superclass descriptor
- .source: associate source filename
- .field: declare fields
- .method … .end method: declare methods
- .annotation … .end annotation: annotations
- .implements: implemented interface descriptor(s)
- .locals N: number of local registers (v0..vN-1)
- .registers N: total registers (parameters + locals) when explicit control is needed
- .line N: source line markers
- .param/.param pX: parameter name/descriptor metadata
- Labels (e.g., :L0): branch targets for if/goto/try regions