Example #1
0
File: hash.c Project: Arkhont/samba
/* Simply overwrite the hash entry we found before. */
enum TDB_ERROR replace_in_hash(struct tdb_context *tdb,
			       struct hash_info *h,
			       tdb_off_t new_off)
{
	return tdb_write_off(tdb, hbucket_off(h->group_start, h->found_bucket),
			     encode_offset(new_off, h));
}
Example #2
0
/*
 * parse a function and its arguments and fill the structure
 */
int encode_function(char *string, struct filter_op *fop)
{
   char *str = strdup(string);
   int ret = -ENOTFOUND;
   char *name, *args;
   int nargs = 0, i;
   char **dec_args = NULL;
   char *tok;

   memset(fop, 0, sizeof(struct filter_op));
   
   /* get the name of the function */
   name = ec_strtok(string, "(", &tok);
   /* get all the args */
   args = name + strlen(name) + 1;

   /* analyze the arguments */
   dec_args = decode_args(args, &nargs);

   /* this fop is a function */
   fop->opcode = FOP_FUNC;

   /* check if it is a known function */
   if (!strcmp(name, "search")) {
      if (nargs == 2) {
         /* get the level (DATA or DECODED) */
         if (encode_offset(dec_args[0], fop) == ESUCCESS) {
            /* encode offset wipe the fop !! */
            fop->opcode = FOP_FUNC;
            fop->op.func.op = FFUNC_SEARCH;
            fop->op.func.string = (u_char*)strdup(dec_args[1]);
            fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string);
            ret = ESUCCESS;
         } else
            SCRIPT_ERROR("Unknown offset %s ", dec_args[0]);
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "regex")) {
      if (nargs == 2) {
         int err;
         regex_t regex;
         char errbuf[100];
         
         /* get the level (DATA or DECODED) */
         if (encode_offset(dec_args[0], fop) == ESUCCESS) {
            /* encode offset wipe the fop !! */
            fop->opcode = FOP_FUNC;
            fop->op.func.op = FFUNC_REGEX;
            fop->op.func.string = (u_char*)strdup(dec_args[1]);
            fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string);
            ret = ESUCCESS;
         } else
            SCRIPT_ERROR("Unknown offset %s ", dec_args[0]);

         /* check if the regex is valid */
         err = regcomp(&regex, (const char*)fop->op.func.string, REG_EXTENDED | REG_NOSUB | REG_ICASE );
         if (err) {
            regerror(err, &regex, errbuf, sizeof(errbuf));
            SCRIPT_ERROR("%s", errbuf);
         } 
         
         regfree(&regex);
                        
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "pcre_regex")) {
#ifndef HAVE_PCRE
      WARNING("The script contains pcre_regex, but you don't have support for it.");
#else
      pcre *pregex;
      const char *errbuf = NULL;
      int erroff;
      
      if (nargs == 2) {
                     
         /* get the level (DATA or DECODED) */
         if (encode_offset(dec_args[0], fop) == ESUCCESS) {
            /* encode offset wipe the fop !! */
            fop->opcode = FOP_FUNC;
            fop->op.func.op = FFUNC_PCRE;
            fop->op.func.string = strdup(dec_args[1]);
            fop->op.func.slen = strlen(fop->op.func.string);
            ret = ESUCCESS;
         } else
            SCRIPT_ERROR("Unknown offset %s ", dec_args[0]);

         /* check if the pcre is valid */
         pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL );
         if (pregex == NULL)
            SCRIPT_ERROR("%s\n", errbuf);

         pcre_free(pregex);
      } else if (nargs == 3) {
            
         fop->opcode = FOP_FUNC;
         fop->op.func.op = FFUNC_PCRE;
         /* substitution always at layer DATA */
         fop->op.func.level = 5;
         fop->op.func.string = strdup(dec_args[1]);
         fop->op.func.slen = strlen(fop->op.func.string);
         fop->op.func.replace = strdup(dec_args[2]);
         fop->op.func.rlen = strlen(fop->op.func.replace);
         ret = ESUCCESS;
         
         /* check if the pcre is valid */
         pregex = pcre_compile(fop->op.func.string, 0, &errbuf, &erroff, NULL );
         if (pregex == NULL)
            SCRIPT_ERROR("%s\n", errbuf);

         pcre_free(pregex);
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
#endif
   } else if (!strcmp(name, "replace")) {
      if (nargs == 2) {
         fop->op.func.op = FFUNC_REPLACE;
         /* replace always operate at DATA level */
         fop->op.func.level = 5;
         fop->op.func.string = (u_char*)strdup(dec_args[0]);
         fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string);
         fop->op.func.replace = (u_char*)strdup(dec_args[1]);
         fop->op.func.rlen = strescape((char*)fop->op.func.replace, (char*)fop->op.func.replace);
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "inject")) {
      if (nargs == 1) {
         fop->op.func.op = FFUNC_INJECT;
         /* inject always operate at DATA level */
         fop->op.func.level = 5;
         fop->op.func.string = (u_char*)strdup(dec_args[0]);
         fop->op.func.slen = strlen((const char*)fop->op.func.string);
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "execinject")) {
      if (nargs == 1) {
         fop->op.func.op = FFUNC_EXECINJECT;
         /* execinject always operate at DATA level */
         fop->op.func.level = 5;
         fop->op.func.string = (u_char*)strdup(dec_args[0]);
         fop->op.func.slen = strlen((const char*)fop->op.func.string);
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "log")) {
      if (nargs == 2) {
         /* get the level (DATA or DECODED) */
         if (encode_offset(dec_args[0], fop) == ESUCCESS) {
            /* encode offset wipe the fop !! */
            fop->opcode = FOP_FUNC;
            fop->op.func.op = FFUNC_LOG;
            fop->op.func.string = (u_char*)strdup(dec_args[1]);
            fop->op.func.slen = strlen((const char*)fop->op.func.string);
            ret = ESUCCESS;
         } else
            SCRIPT_ERROR("Unknown offset %s ", dec_args[0]);
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "drop")) {
      if (nargs == 0) {
         fop->op.func.op = FFUNC_DROP;
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "kill")) {
      if (nargs == 0) {
         fop->op.func.op = FFUNC_KILL;
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "msg")) {
      if (nargs == 1) {
         fop->op.func.op = FFUNC_MSG;
         fop->op.func.string = (u_char*)strdup(dec_args[0]);
         fop->op.func.slen = strescape((char*)fop->op.func.string, (char*)fop->op.func.string);
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "exec")) {
      if (nargs == 1) {
         fop->op.func.op = FFUNC_EXEC;
         fop->op.func.string = (u_char*)strdup(dec_args[0]);
         fop->op.func.slen = strlen((const char*)fop->op.func.string);
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   } else if (!strcmp(name, "exit")) {
      if (nargs == 0) {
         fop->opcode = FOP_EXIT;
         ret = ESUCCESS;
      } else
         SCRIPT_ERROR("Wrong number of arguments for function \"%s\" ", name);
   }

   /* free the array */
   for (i = 0; i < nargs; i++)
      SAFE_FREE(dec_args[i]);
      
   SAFE_FREE(dec_args);
   SAFE_FREE(str);
   return ret;
}
Example #3
0
bool MethodTransform::try_sync() {
  TRACE(MTRANS, 5, "Syncing %s\n", SHOW(m_method));
  auto code = m_method->get_code();
  auto& opout = code->get_instructions();
  opout.clear();
  uint32_t addr = 0;
  addr_mei_t addr_to_mei;
  // Step 1, regenerate opcode list for the method, and
  // and calculate the opcode entries address offsets.
  TRACE(MTRANS, 5, "Emitting opcodes\n");
  for (auto miter = m_fmethod->begin(); miter != m_fmethod->end(); miter++) {
    MethodItemEntry* mentry = &*miter;
    TRACE(MTRANS, 5, "Analyzing mentry %p\n", mentry);
    mentry->addr = addr;
    if (mentry->type == MFLOW_OPCODE) {
      if ((mentry->insn->opcode() == FOPCODE_FILLED_ARRAY) && (addr & 1)) {
        opout.push_back(new DexInstruction(OPCODE_NOP));
        ++addr;
      }
      addr_to_mei[addr] = mentry;
      TRACE(MTRANS, 5, "Emitting mentry %p at %08x\n", mentry, addr);
      opout.push_back(mentry->insn);
      addr += mentry->insn->size();
    }
  }
  // Step 2, recalculate branches..., save off multi-branch data.
  TRACE(MTRANS, 5, "Recalculating branches\n");
  std::vector<MethodItemEntry*> multi_branches;
  std::unordered_map<MethodItemEntry*, std::vector<BranchTarget*>> multis;
  std::unordered_map<BranchTarget*, uint32_t> multi_targets;
  std::unordered_map<DexTryItem*, std::vector<MethodItemEntry*>> try_items;
  for (auto miter = m_fmethod->begin(); miter != m_fmethod->end(); miter++) {
    MethodItemEntry* mentry = &*miter;
    if (mentry->type == MFLOW_OPCODE) {
      auto opcode = mentry->insn->opcode();
      if (is_branch(opcode) && is_multi_branch(opcode)) {
        multi_branches.push_back(mentry);
      }
    }
    if (mentry->type == MFLOW_TARGET) {
      BranchTarget* bt = mentry->target;
      if (bt->type == BRANCH_MULTI) {
        multis[bt->src].push_back(bt);
        multi_targets[bt] = mentry->addr;
        // We can't fix the primary switch opcodes address until we emit
        // the fopcode, which comes later.
      } else if (bt->type == BRANCH_SIMPLE) {
        MethodItemEntry* tomutate = bt->src;
        int32_t branchoffset = mentry->addr - tomutate->addr;
        if ((tomutate->insn->opcode() == OPCODE_FILL_ARRAY_DATA) &&
            (mentry->addr & 1)) {
          ++branchoffset; // account for nop spacer
        }
        auto encode_result = encode_offset(tomutate->insn, branchoffset);
        if (!encode_result.success) {
          auto inst = tomutate->insn;
          tomutate->insn = new DexInstruction(encode_result.newopcode);
          delete inst;
          return false;
        }
      }
    }
    if (mentry->type == MFLOW_TRY) {
      try_items[mentry->tentry->tentry].push_back(mentry);
    }
  }
  TRACE(MTRANS, 5, "Emitting multi-branches\n");
  // Step 3, generate multi-branch fopcodes
  for (auto multiopcode : multi_branches) {
    auto targets = multis[multiopcode];
    std::sort(targets.begin(), targets.end(), multi_target_compare_index);
    if (multi_contains_gaps(targets)) {
      // Emit sparse.
      unsigned long count = (targets.size() * 4) + 2;
      uint16_t sparse_payload[count];
      sparse_payload[0] = FOPCODE_SPARSE_SWITCH;
      sparse_payload[1] = targets.size();
      uint32_t* spkeys = (uint32_t*)&sparse_payload[2];
      uint32_t* sptargets =
          (uint32_t*)&sparse_payload[2 + (targets.size() * 2)];
      for (auto target : targets) {
        *spkeys++ = target->index;
        *sptargets++ = multi_targets[target] - multiopcode->addr;
      }
      // Emit align nop
      if (addr & 1) {
        DexInstruction* nop = new DexInstruction(0);
        opout.push_back(nop);
        addr++;
      }
      // Insert the new fopcode...
      DexInstruction* fop = new DexOpcodeData(sparse_payload, (int) (count - 1));
      opout.push_back(fop);
      // re-write the source opcode with the address of the
      // fopcode, increment the address of the fopcode.
      encode_offset(multiopcode->insn, addr - multiopcode->addr);
      multiopcode->insn->set_opcode(OPCODE_SPARSE_SWITCH);
      addr += count;
    } else {
      // Emit packed.
      unsigned long count = (targets.size() * 2) + 4;
      uint16_t packed_payload[count];
      packed_payload[0] = FOPCODE_PACKED_SWITCH;
      packed_payload[1] = targets.size();
      uint32_t* psdata = (uint32_t*)&packed_payload[2];
      *psdata++ = targets.front()->index;
      for (auto target : targets) {
        *psdata++ = multi_targets[target] - multiopcode->addr;
      }
      // Emit align nop
      if (addr & 1) {
        DexInstruction* nop = new DexInstruction(0);
        opout.push_back(nop);
        addr++;
      }
      // Insert the new fopcode...
      DexInstruction* fop = new DexOpcodeData(packed_payload, (int) (count - 1));
      opout.push_back(fop);
      // re-write the source opcode with the address of the
      // fopcode, increment the address of the fopcode.
      encode_offset(multiopcode->insn, addr - multiopcode->addr);
      multiopcode->insn->set_opcode(OPCODE_PACKED_SWITCH);
      addr += count;
    }
  }

  // Step 4, emit debug opcodes
  TRACE(MTRANS, 5, "Emitting debug opcodes\n");
  auto debugitem = code->get_debug_item();
  if (debugitem) {
    auto& entries = debugitem->get_entries();
    entries.clear();
    for (auto& mentry : *m_fmethod) {
      if (mentry.type == MFLOW_DEBUG) {
        entries.emplace_back(mentry.addr, mentry.dbgop->clone());
      } else if (mentry.type == MFLOW_POSITION) {
        entries.emplace_back(mentry.addr, mentry.pos);
      }
    }
  }
  // Step 5, try/catch blocks
  auto& tries = code->get_tries();
  tries.clear();
  for (auto tryitem : try_items) {
    DexTryItem* dextry = tryitem.first;
    auto& tryentries = tryitem.second;
    sort(tryentries.begin(), tryentries.end(), order_try_entries);
    dextry->m_catches.clear();
    MethodItemEntry* try_start = nullptr;
    bool suppress = false;
    for (auto tryentry : tryentries) {
      switch (tryentry->tentry->type) {
      case TRY_START:
        dextry->m_start_addr = tryentry->addr;
        try_start = tryentry;
        break;
      case TRY_END:
        assert(try_start != nullptr);
        assert(try_start->addr <= tryentry->addr);
        dextry->m_insn_count = tryentry->addr - try_start->addr;
        if (dextry->m_insn_count == 0) {
          suppress = true;
        }
        break;
      case TRY_CATCH:
        if (tryentry->tentry->centry == nullptr) {
          /* Catch all */
          dextry->m_catchall = tryentry->addr;
        } else {
          dextry->m_catches.push_back(
              std::make_pair(tryentry->tentry->centry, tryentry->addr));
        }
        break;
      default:
        always_assert_log(false, "Invalid try entry type");
      }
    }
    if (!suppress) {
      tries.push_back(dextry);
    }
  }
  std::sort(tries.begin(),
            tries.end(),
            [](const DexTryItem* a, const DexTryItem* b) {
              return a->m_start_addr < b->m_start_addr;
            });
  return true;
}
Example #4
0
bool IRCode::try_sync(DexCode* code) {
  std::unordered_map<MethodItemEntry*, uint32_t> entry_to_addr;
  uint32_t addr = 0;
  // Step 1, regenerate opcode list for the method, and
  // and calculate the opcode entries address offsets.
  TRACE(MTRANS, 5, "Emitting opcodes\n");
  for (auto miter = m_ir_list->begin(); miter != m_ir_list->end(); ++miter) {
    MethodItemEntry* mentry = &*miter;
    TRACE(MTRANS, 5, "Analyzing mentry %p\n", mentry);
    entry_to_addr[mentry] = addr;
    if (mentry->type == MFLOW_DEX_OPCODE) {
      TRACE(MTRANS, 5, "Emitting mentry %p at %08x\n", mentry, addr);
      addr += mentry->dex_insn->size();
    }
  }
  // Step 2, Branch relaxation: calculate branch offsets for if-* and goto
  // opcodes, resizing them where necessary. Since resizing opcodes affects
  // address offsets, we need to iterate this to a fixed point.
  //
  // For instructions that use address offsets but never need resizing (i.e.
  // switch and fill-array-data opcodes), we calculate their offsets after
  // we have reached the fixed point.
  TRACE(MTRANS, 5, "Recalculating branches\n");
  std::vector<MethodItemEntry*> multi_branches;
  std::unordered_map<MethodItemEntry*, std::vector<BranchTarget*>> multis;
  std::unordered_map<BranchTarget*, uint32_t> multi_targets;
  bool needs_resync = false;
  for (auto miter = m_ir_list->begin(); miter != m_ir_list->end(); ++miter) {
    MethodItemEntry* mentry = &*miter;
    if (entry_to_addr.find(mentry) == entry_to_addr.end()) {
      continue;
    }
    if (mentry->type == MFLOW_DEX_OPCODE) {
      auto opcode = mentry->dex_insn->opcode();
      if (dex_opcode::is_switch(opcode)) {
        multi_branches.push_back(mentry);
      }
    }
    if (mentry->type == MFLOW_TARGET) {
      BranchTarget* bt = mentry->target;
      if (bt->type == BRANCH_MULTI) {
        multis[bt->src].push_back(bt);
        multi_targets[bt] = entry_to_addr.at(mentry);
        // We can't fix the primary switch opcodes address until we emit
        // the fopcode, which comes later.
      } else if (bt->type == BRANCH_SIMPLE &&
                 dex_opcode::is_branch(bt->src->dex_insn->opcode())) {
        MethodItemEntry* branch_op_mie = bt->src;
        auto branch_addr = entry_to_addr.find(branch_op_mie);
        always_assert_log(branch_addr != entry_to_addr.end(),
                          "%s refers to nonexistent branch instruction",
                          SHOW(*mentry));
        int32_t branch_offset = entry_to_addr.at(mentry) - branch_addr->second;
        needs_resync |= !encode_offset(m_ir_list, mentry, branch_offset);
      }
    }
  }
  if (needs_resync) {
    return false;
  }

  size_t num_align_nops{0};
  auto& opout = code->reset_instructions();
  for (auto& mie : *m_ir_list) {
    // We are assuming that fill-array-data-payload opcodes are always at
    // the end of the opcode stream (we enforce that during instruction
    // lowering). I.e. they are only followed by other fill-array-data-payload
    // opcodes. So adjusting their addresses here does not require re-running
    // branch relaxation.
    entry_to_addr.at(&mie) += num_align_nops;
    if (mie.type == MFLOW_TARGET &&
        mie.target->src->dex_insn->opcode() == DOPCODE_FILL_ARRAY_DATA) {
      // This MFLOW_TARGET is right before a fill-array-data-payload opcode,
      // so we should make sure its address is aligned
      if (entry_to_addr.at(&mie) & 1) {
        opout.push_back(new DexInstruction(DOPCODE_NOP));
        ++entry_to_addr.at(&mie);
        ++num_align_nops;
      }
      mie.target->src->dex_insn->set_offset(entry_to_addr.at(&mie) -
                                            entry_to_addr.at(mie.target->src));
      continue;
    }
    if (mie.type != MFLOW_DEX_OPCODE) {
      continue;
    }
    TRACE(MTRANS, 6, "Emitting insn %s\n", SHOW(mie.dex_insn));
    opout.push_back(mie.dex_insn);
  }
  addr += num_align_nops;

  TRACE(MTRANS, 5, "Emitting multi-branches\n");
  // Step 3, generate multi-branch fopcodes
  for (auto multiopcode : multi_branches) {
    auto& targets = multis[multiopcode];
    auto multi_insn = multiopcode->dex_insn;
    std::sort(targets.begin(), targets.end(), multi_target_compare_case_key);
    always_assert_log(
        !targets.empty(), "need to have targets for %s", SHOW(*multiopcode));
    if (sufficiently_sparse(targets)) {
      // Emit sparse.
      const size_t count = (targets.size() * 4) + 2;
      auto sparse_payload = std::make_unique<uint16_t[]>(count);
      sparse_payload[0] = FOPCODE_SPARSE_SWITCH;
      sparse_payload[1] = targets.size();
      uint32_t* spkeys = (uint32_t*)&sparse_payload[2];
      uint32_t* sptargets =
          (uint32_t*)&sparse_payload[2 + (targets.size() * 2)];
      for (BranchTarget* target : targets) {
        *spkeys++ = target->case_key;
        *sptargets++ = multi_targets[target] - entry_to_addr.at(multiopcode);
      }
      // Emit align nop
      if (addr & 1) {
        DexInstruction* nop = new DexInstruction(DOPCODE_NOP);
        opout.push_back(nop);
        addr++;
      }
      // Insert the new fopcode...
      DexInstruction* fop =
          new DexOpcodeData(sparse_payload.get(), (int)(count - 1));
      opout.push_back(fop);
      // re-write the source opcode with the address of the
      // fopcode, increment the address of the fopcode.
      multi_insn->set_offset(addr - entry_to_addr.at(multiopcode));
      multi_insn->set_opcode(DOPCODE_SPARSE_SWITCH);
      addr += count;
    } else {
      // Emit packed.
      const uint64_t size = get_packed_switch_size(targets);
      always_assert(size <= std::numeric_limits<uint16_t>::max());
      const size_t count = (size * 2) + 4;
      auto packed_payload = std::make_unique<uint16_t[]>(count);
      packed_payload[0] = FOPCODE_PACKED_SWITCH;
      packed_payload[1] = size;
      uint32_t* psdata = (uint32_t*)&packed_payload[2];
      int32_t next_key = *psdata++ = targets.front()->case_key;
      for (BranchTarget* target : targets) {
        // Fill in holes with relative offsets that are falling through to the
        // instruction after the switch instruction
        for (; next_key != target->case_key; ++next_key) {
          *psdata++ = 3; // packed-switch statement is three code units
        }
        *psdata++ = multi_targets[target] - entry_to_addr.at(multiopcode);
        ++next_key;
      }
      // Emit align nop
      if (addr & 1) {
        DexInstruction* nop = new DexInstruction(DOPCODE_NOP);
        opout.push_back(nop);
        addr++;
      }
      // Insert the new fopcode...
      DexInstruction* fop =
          new DexOpcodeData(packed_payload.get(), (int)(count - 1));
      opout.push_back(fop);
      // re-write the source opcode with the address of the
      // fopcode, increment the address of the fopcode.
      multi_insn->set_offset(addr - entry_to_addr.at(multiopcode));
      multi_insn->set_opcode(DOPCODE_PACKED_SWITCH);
      addr += count;
    }
  }

  // Step 4, emit debug entries
  TRACE(MTRANS, 5, "Emitting debug entries\n");
  auto debugitem = code->get_debug_item();
  if (debugitem) {
    gather_debug_entries(m_ir_list, entry_to_addr, &debugitem->get_entries());
  }
  // Step 5, try/catch blocks
  TRACE(MTRANS, 5, "Emitting try items & catch handlers\n");
  auto& tries = code->get_tries();
  tries.clear();
  MethodItemEntry* active_try = nullptr;
  for (auto& mentry : *m_ir_list) {
    if (mentry.type != MFLOW_TRY) {
      continue;
    }
    auto& tentry = mentry.tentry;
    if (tentry->type == TRY_START) {
      always_assert(active_try == nullptr);
      active_try = &mentry;
      continue;
    }
    redex_assert(tentry->type == TRY_END);
    auto try_end = &mentry;
    auto try_start = active_try;
    active_try = nullptr;

    always_assert_log(
        try_start != nullptr, "unopened try_end found: %s", SHOW(*try_end));
    always_assert_log(try_start->tentry->catch_start ==
                          try_end->tentry->catch_start,
                      "mismatched try start (%s) and end (%s)",
                      SHOW(*try_start),
                      SHOW(*try_end));
    auto start_addr = entry_to_addr.at(try_start);
    auto end_addr = entry_to_addr.at(try_end);
    if (start_addr == end_addr) {
      continue;
    }

    DexCatches catches;
    for (auto mei = try_end->tentry->catch_start;
        mei != nullptr;
        mei = mei->centry->next) {
      if (mei->centry->next != nullptr) {
        always_assert(mei->centry->catch_type != nullptr);
      }
      catches.emplace_back(mei->centry->catch_type, entry_to_addr.at(mei));
    }
    split_and_insert_try_regions(start_addr, end_addr, catches, &tries);
  }
  always_assert_log(active_try == nullptr, "unclosed try_start found");

  std::sort(tries.begin(),
            tries.end(),
            [](const std::unique_ptr<DexTryItem>& a,
               const std::unique_ptr<DexTryItem>& b) {
              return a->m_start_addr < b->m_start_addr;
            });
  return true;
}