Skip to content

🏷️ LabelMethodItem

方法体内所有标签(:goto_0:try_start_0:catch_0 等)的统一表示,通过 LabelCache 去重内化。

属性
完整类名org.jf.baksmali.Adaptors.LabelMethodItem
源码链接Adaptors/LabelMethodItem.java
继承MethodItem
sortOrder0(在 debug 信息之后,指令之前)

🎯 职责

LabelMethodItem 代表 smali 中的跳转标签(: 开头的行)。它有以下特性:

  1. 去重内化:通过 MethodDefinition.LabelCache.internLabel() 保证同地址同前缀只有一个实例
  2. 两种编号模式useSequentialLabels 为 false 时用 code address 的十六进制(:goto_4),为 true 时用从 0 开始的序号(:goto_0
  3. equals/hashCode 重写:基于 (codeAddress, labelPrefix) 实现内化的 key 语义

🧠 关键实现

writeTo — 标签输出

java
public boolean writeTo(IndentingWriter writer) throws IOException {
    writer.write(':');
    writer.write(labelPrefix);
    if (options.useSequentialLabels) {
        writer.printUnsignedLongAsHex(labelSequence);
    } else {
        writer.printUnsignedLongAsHex(this.getLabelAddress());
    }
    return true;
}

输出示例(useSequentialLabels=false)::goto_4:try_start_a:catch_1e:pswitch_0

equals/hashCode 保证去重

java
public boolean equals(Object o) {
    if (!(o instanceof LabelMethodItem)) {
        return false;
    }
    return this.compareTo((MethodItem)o) == 0;
}

public int hashCode() {
    return getCodeAddress();  // 同地址的标签强制走 equals 比较
}

内化流程

java
// 在 CatchMethodItem、InstructionMethodItem(offset 指令)、PackedSwitchMethodItem 中:
LabelMethodItem label = methodDef.getLabelCache().internLabel(
    new LabelMethodItem(options, targetAddress, "goto_"));

所有引用同一 (address, prefix) 的调用都会得到同一个 LabelMethodItem 对象,确保在输出列表中只出现一次。


📋 标签前缀清单

前缀来源示例
goto_goto/if 跳转目标:goto_4
cond_条件跳转(备用):cond_0
try_start_try 块开始:try_start_0
try_end_try 块结束:try_end_8
catch_异常处理器入口:catch_c
catchall_catch-all 入口:catchall_0
pswitch_packed-switch case 目标:pswitch_14
sswitch_sparse-switch case 目标:sswitch_0

🔗 关系


📌 小结

LabelMethodItem 是 smali 方法体控制流可读性的核心——它将 DEX 中的字节码偏移转换为有意义的标签名称。LabelCache 的内化机制保证了多个引用同一目标的指令(如多个 if-* 指向同一处)在输出中只产生一个标签行,而不是重复的同名标签。

基于 MIT 协议发布 · 本文档仅用于安全研究与教学目的