Background

This pass looks for analyzing all exceptions with pure string as constructors and re-mapping them using fewer characters. In java code, we will often see some exceptions with long strings as constructor. Such as: (Java code)

throw new IllegalArgumentException("tag must not be null or empty");
const-string v1, "tag must not be null or empty"
new-instance v0, Ljava/lang/IllegalArgumentException;
invoke-direct {v0, v1}, Ljava/lang/IllegalArgumentException;-><init>(Ljava/lang/String;)V
throw v0

Also, in code what generated by Kotlin, we will see some exceptions generated by Kotlin compiler. Such as null cannot be cast to non-null.

Implement Details

Find all const-string used with single usage

traverse all const-string in cfg structure, and use use-def chains to only use once.

live_range::MoveAwareChains chains(cfg);
auto du_chains = chains.get_def_use_chains();
if (!du_chains.count(insn)) {
  continue;
}
auto user = du_chains.at(insn);
if (user.size() > 1) {
  continue;
}

Filter them with single string as parameter constructor

Analysis class as exception’s subclass and has single string as constructor parameter.

 auto more_checks = [](IRInstruction* it) {
    // Check is named as Exception.
    auto type = it->get_method()->get_class();
    auto exception = type_class(DexType::get_type("Ljava/lang/Exception;"));
    auto cls = type_class(type);
    if (type->str().find("Exception;") == std::string::npos &&
        has_class_in_hierarchy(cls, exception)) {
      return false;
    }
    // Check arguments list and only one string arg.
    auto args = it->get_method()->get_proto()->get_args();
    if (args->size() != 1 || args->at(0) != type::java_lang_String()) {
      return false;
    }

    return true;
  };

Also, we could run tik tok with this pass to generate exceptions profile file to speed up pass search exceptions. I got this result in Tik Tok code :

  auto exception_cls_name = std::vector<std::string>{
      "Landroid/accounts/NetworkErrorException;",
      "Landroid/database/sqlite/SQLiteException;",
      "Landroid/os/RemoteException;",
      "Landroid/util/AndroidRuntimeException;",
      "Landroid/view/InflateException;",
      "Lcom/facebook/jni/CppException;",
...

Clean/Remapping with new symbol

auto it = user.begin()->insn;
if (opcode::is_invoke_direct(it->opcode()) &&
    (in_list(it->get_method()) || more_checks(it))) {
  TRACE(STRBUILD, 1, "CS_STR %s ", str.c_str());
  exceptions.emplace(it->get_method()->get_class()->str());
  insn->set_string(empty);
}
  • use profile list to quick-search
  • clean or re-generate ids to re-mapping.