view paraspace/injection.py @ 149:d4533a59c694

inject_classdefs() also inject relative _DEX_StringIds.
author Thinker K.F. Li <thinker@codemud.net>
date Mon, 15 Aug 2011 21:51:39 +0800
parents 90e181f2a0c5
children 1eb1b2ca5de4
line wrap: on
line source


def _relocatable_children(obj):
    from paraspace.dex_deptracker import _dex_tree_get_child
    from paraspace.dexfile import relocatable, array

    if isinstance(obj, array):
        rel_children = [('items.' + str(idx), value)
                        for idx, value in enumerate(obj.items)
                        if isinstance(value, relocatable)]
        return rel_children
    
    attrs = obj.children()
    attr_value_pairs = [(attr, _dex_tree_get_child(obj, attr))
                        for attr in attrs]
    rel_children = [(attr, value) for attr, value in attr_value_pairs
                    if isinstance(value, relocatable)]
    return rel_children


## \brief Travel relocatable descendants.
#
# \param cloner is the function to return a clone.
# \param adjuster is called to adjust the clone.
# \param visit_log is a dictionary to keep clones.
#
def _travel_desc_relocatable(obj, worker, visit_log):
    if id(obj) in visit_log:
        return visit_log[id(obj)]

    result = worker(obj)
    visit_log[id(obj)] = result
    
    rel_children = _relocatable_children(obj)
    for attr, value in rel_children:
        _travel_desc_relocatable(value, worker, visit_log)
        pass
    pass


## \brief Return name string of a linked class definition item.
def classdef_name(classdef):
    return classdef.classIdx.descriptorIdx.stringDataOff.data


## \brief Return a map that map type of a object to the list of a DEXFile.
def dex_type_2_array_attr_map():
    global dex_type_2_array_attr_map
    from paraspace.dexfile import DEXFile, array
    from paraspace.dex_deptracker import _marker

    def skip_marker_type(clazz):
        while isinstance(clazz, _marker):
            clazz = clazz.back_type
            pass
        return clazz
    
    attr_values = [(attr, skip_marker_type(getattr(DEXFile, attr)))
                   for attr in dir(DEXFile)]
    array_attrs = [(skip_marker_type(value.child_type), attr)
                   for attr, value in attr_values
                   if isinstance(value, array)]
    type_2_attr = dict(array_attrs)
    
    dex_type_2_array_attr_map = lambda: type_2_attr
    
    return type_2_attr


_saved_dex_type_2_array_attr_map = dex_type_2_array_attr_map


## \brief Append a object to appropriate list of a DEXFile object.
#
# Skip the object if found no appropriate list.
#
def dex_append_obj_list(dex, obj):
    from paraspace.dex_deptracker import _dex_tree_get_child
    from paraspace.dex_deptracker import _dex_tree_set_child
    from paraspace.dexfile import _DEX_TypeList, _DEX_TypeList_align
    from paraspace.dexfile import array_sorted

    ret_obj = obj
    if isinstance(obj, _DEX_TypeList):
        wrapper = _DEX_TypeList_align()
        wrapper.value = obj
        obj = wrapper
        pass

    type_2_attr = dex_type_2_array_attr_map()
    try:
        attr = type_2_attr[obj.__class__]
    except KeyError:
        return

    array = getattr(dex, attr)
    
    if isinstance(array, array_sorted):
        if obj in array.items:
            idx = array.items.index(obj)
            obj = array.items[idx]
            if isinstance(obj, _DEX_TypeList_align):
                return obj.value
            return obj
        pass

    array.items.append(obj)
    
    count_name = array.count_name
    if count_name:
        count = _dex_tree_get_child(dex, count_name)
        _dex_tree_set_child(dex, count_name, count + 1)
        pass

    return ret_obj


## \brief Clone a composite object.
#
# \param dex is the DEXFile that the composite object is cloning for.
# \param comobj is composite object that is cloning.
#
def _clone_composite(dex, comobj):
    from copy import copy
    from paraspace.dexfile import _DEX_StringDataItem, _DEX_StringId
    from paraspace.dexfile import _DEX_TypeId
    from paraspace.dex_deptracker import _dex_tree_set_child
    from paraspace.dex_deptracker import _dex_tree_get_child
    
    visit_log = {}
    
    def cloner(obj):
        clone = copy(obj)
        return clone

    def relink_dependencies(clone):
        rel_children = _relocatable_children(clone)
        for attr, value in rel_children:
            clone_value = visit_log[id(value)]
            _dex_tree_set_child(clone, attr, clone_value)
            pass
        pass

    def merge_unique_strdata():
        strdatas = [(obj_id, obj)
                    for obj_id, obj in visit_log.items()
                    if isinstance(obj, _DEX_StringDataItem)]
        dex_str_2_strdata = dict([(strdata.data.data, strdata)
                                  for strdata in dex.stringDataItems.items])
        for obj_id, strdata in strdatas:
            if strdata.data.data in dex_str_2_strdata:
                visit_log[obj_id] = dex_str_2_strdata[strdata.data.data]
            else:
                dex_append_obj_list(dex, strdata)
                pass
            pass
        pass

    def merge_unique_strid():
        strids = [(obj_id, obj)
                  for obj_id, obj in visit_log.items()
                  if isinstance(obj, _DEX_StringId)]
        
        for obj_id, strid in strids:
            relink_dependencies(strid)
            pass

        strdata_2_strid = dict([(strid.stringDataOff, strid)
                                for strid in dex.stringIds.items])
        for obj_id, strid in strids:
            if strid.stringDataOff in strdata_2_strid:
                visit_log[obj_id] = strdata_2_strid[strid.stringDataOff]
            else:
                dex_append_obj_list(dex, strid)
                pass
            pass
        pass

    def merge_unique_typeid():
        typeids = [(obj_id, obj)
                   for obj_id, obj in visit_log.items()
                   if isinstance(obj, _DEX_TypeId)]
        
        for obj_id, typeid in typeids:
            relink_dependencies(typeid)
            pass

        strid_2_typeid = dict([(typeid.descriptorIdx, typeid)
                               for typeid in dex.typeIds.items])
        for obj_id, typeid in typeids:
            if typeid.descriptorIdx in strid_2_typeid:
                visit_log[obj_id] = strid_2_typeid[typeid.descriptorIdx]
            else:
                dex_append_obj_list(dex, typeid)
                pass
            pass
        pass

    _travel_desc_relocatable(comobj, cloner, visit_log)

    merge_unique_strdata()
    merge_unique_strid()
    merge_unique_typeid()
    
    relink_list = []
    for key, obj in visit_log.items():
        if isinstance(obj, (_DEX_StringDataItem,
                            _DEX_StringId,
                            _DEX_TypeId)):
            continue
        r = dex_append_obj_list(dex, obj)
        if r and r is not obj:
            visit_log[key] = r
        else:
            relink_list.append(obj)
            pass
        pass
    
    for obj in relink_list:
        if isinstance(obj, (_DEX_StringDataItem,
                            _DEX_StringId,
                            _DEX_TypeId)):
            continue
        relink_dependencies(obj)
        pass
    
    clone = visit_log[id(comobj)]
    return clone


## \brief Clone a class definition item
#
# \param dex is the DEXFile that clazz is cloning for.
# \param clazz is the class definition item that is cloning.
# \return the cloning _DEX_ClassDef.
#
def _clone_classdef(dex, clazz):
    from paraspace.dexfile import DEXFile_linked, _DEX_ClassDef
    
    def has_classdef(clazz):
        classname = DEXFile_linked.get_classdef_name(clazz)
        try:
            dex.find_class_name(classname)
        except ValueError:
            return False
        return True

    assert isinstance(clazz, _DEX_ClassDef)

    if has_classdef(clazz):
        raise RuntimeError, \
            'clone a class \'%s\'that is already in the DEXFile' % \
            classdef_name(clazz)
    
    clone = _clone_composite(dex, clazz)
    return clone


## \brief Clone a class definition and insert into a DEXFile.
#
# This function clone a class definition from a linked DEXFile and
# insert it into another one.
#
# \param dex is a DEXFile_linked to insert the clone.
# \param class_def is a class definition going to be cloned.
#
def dexfile_insert_class(dex, classdef):
    clone = _clone_classdef(dex, classdef)
    return clone


## \brief Collect info of classes mentioned by the code of given class.
def _find_class_relative(dex, classdef):
    def classify_typeids_defined(dex, typeids):
        classdefs = []
        undef_typeids = []
        for typeid in typeids:
            try:
                classdef = dex.find_class_typeid(typeid)
            except ValueError:
                undef_typeids.append(typeid)
            else:
                classdefs.append(classdef)
                pass
            pass
        return classdefs, undef_typeids

    typeidxs, stridxs = \
        collect_typeidxs_stridxs_mentioned_by_class(dex, classdef)
    typeids = [dex.find_typeid_idx(typeidx)
               for typeidx in typeidxs]
    strids = [dex.find_strid_idx(idx) for idx in stridxs]
    
    classdefs, typeids = classify_typeids_defined(dex, typeids)
    
    return classdefs, typeids, strids


def dexfile_insert_classdefs(dex_dst, dex_src, classdefs):
    clones = [dexfile_insert_class(dex_dst, classdef)
              for classdef in classdefs]
    return clones


## \brief Insert _DEX_StringId into another DEX.
def dexfile_insert_stringid(dex_dst, dex_src, strid):
    clone = _clone_composite(dex_dst, strid)
    return clone


## \brief Insert _DEX_StringIds into another DEX.
def dexfile_insert_stringids(dex_dst, dex_src, strids):
    clones = [dexfile_insert_stringid(dex_dst, dex_src, strid)
              for strid in strids]
    return clones


## \brief Clone and insert a _DEX_TypeId to another DEXFile_linked.
#
# \param dex_dst is a DEXFile_linked where the cloning one is inserted.
# \param dex_src is a DEXFile_linked where the cloned one is from.
# \param typeid is a _DEX_TypeId that is cloned.
# \return the cloning _DEX_TypeId.
#
def dexfile_insert_or_merge_typeid(dex_dst, dex_src, typeid):
    from paraspace.dexfile import _DEX_TypeId, DEXFile_linked, _DEX_MethodId
    
    assert isinstance(typeid, _DEX_TypeId)
    
    def typeid_not_in_dst(typeid):
        typename = DEXFile_linked.get_typeid_name(typeid)
        try:
            dex_dst.find_typeid_name(typename)
        except ValueError:
            return True
        return False
    
    if typeid_not_in_dst(typeid):
        cloning = _clone_composite(dex_dst, typeid)
        
        methodids = dex_src.find_methodids_typeid(dex_src, typeid)
        for methodid in methodids:
            _clone_composite(dex_dst, methodid)
            pass
        pass
    else:
        typename = DEXFile_linked.get_typeid_name(typeid)
        cloning = dex_dst.find_typeid_name(typename)
        
        methodids = dex_src.find_methodids_typeid(typeid)
        for methodid in methodids:
            methodname = DEXFile_linked.get_methodid_name(methodid)
            try:
                dex_dst.find_methodid_name_proto(methodname, methodid.protoIdx,
                                                 cloning)
            except ValueError:
                cloning_methodid = _clone_composite(dex_dst, methodid)
                pass
            pass
    
    return cloning


## \brief Clone and insert a list of _DEX_TypeId objects to a DEXFile_linked.
def dexfile_insert_or_merge_typeids(dex_dst, dex_src, typeids):
    clones = [dexfile_insert_or_merge_typeid(dex_dst, dex_src, typeid)
              for typeid in typeids]
    return clones


## \brief Collects relative type IDs and classes definition for given class.
def collect_classdefs_relative(dex, classdefs):
    rel_classdefs = set(classdefs)
    rel_typeids = set()
    rel_strids = set()
    
    classdef_queue = list(classdefs)
    while classdef_queue:
        cur_classdef = classdef_queue.pop(0)
        
        classdefs, typeids, strids = _find_class_relative(dex, cur_classdef)
        rel_typeids.update(typeids)
        new_classdefs = list(set(classdefs) - rel_classdefs)
        classdef_queue = classdef_queue + new_classdefs
        rel_classdefs.update(new_classdefs)
        rel_strids.update(strids)
        pass
    return rel_classdefs, rel_typeids, rel_strids


## \brief Clone and insert given and relative classes into another DEXFile.
#
# \param dex_dst is a DEXFile_linked where the class will be inserted.
# \param dex_src is a DEXFile_linked where the cloned class is from.
# \param classdef is a _DEX_ClassDef that will be cloned.
# \return a vector of a list of classdefs and a list of typeids.
#
def dexfile_insert_classdefs_relative(dex_dst, dex_src, classdefs):
    from paraspace.dexfile import DEXFile_linked
    
    def classdef_not_in_dst(classdef):
        classname = DEXFile_linked.get_classdef_name(classdef)
        try:
            dex_dst.find_class_name(classname)
        except ValueError:
            return True
        return False

    relative_classdefs, relative_typeids, relative_strids = \
        collect_classdefs_relative(dex_src, classdefs)
    
    for classdef in relative_classdefs:
        if classdef_not_in_dst(classdef):
            continue
        raise ValueError, '%s is already in DEX %s: can not insert it' % \
            (repr(classdef), repr(dex_dst))
    
    cloning_classdefs = \
        dexfile_insert_classdefs(dex_dst, dex_src, relative_classdefs)
    cloning_typeids = \
        dexfile_insert_or_merge_typeids(dex_dst, dex_src, relative_typeids)
    cloning_strids = \
        dexfile_insert_stringids(dex_dst, dex_src, relative_strids)

    return cloning_classdefs, cloning_typeids, cloning_strids


## \brief Redirect types, methods and strings for the code of given method.
def method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
                            stridxs_redir):
    from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes
    from paraspace.dalvik_opcodes import encode_opcode_vectors
    from paraspace.dexfile import DEXFile_linked
    
    if not method.codeOffRef.is_true:
        return
    
    code = method.codeOffRef.value
    insns_blk = code.insns.data
    op_vectors = decode_insn_blk(insns_blk)

    def redirect(opcode, args):
        if opcode == all_opcodes.OP_NEW_INSTANCE:
            typeidx = args[1]
            if typeidx in typeidxs_redir:
                to_type = typeidxs_redir[typeidx]
                return opcode, (args[0], to_type)
            pass
        elif opcode in (all_opcodes.OP_INVOKE_DIRECT,
                        all_opcodes.OP_INVOKE_VIRTUAL,
                        all_opcodes.OP_INVOKE_SUPER,
                        all_opcodes.OP_INVOKE_STATIC,
                        all_opcodes.OP_INVOKE_INTERFACE):
            methodidx = args[2]
            if methodidx not in methods_redir:
                return opcode, args
            
            return opcode, (args[0], args[1], methods_redir[methodidx],
                            args[3], args[4], args[5], args[6])
        elif opcode in (all_opcodes.OP_INVOKE_VIRTUAL_RANGE,
                        all_opcodes.OP_INVOKE_DIRECT_RANGE,
                        all_opcodes.OP_INVOKE_SUPER_RANGE,
                        all_opcodes.OP_INVOKE_STATIC_RANGE,
                        all_opcodes.OP_INVOKE_INTERFACE_RANGE):
            methodidx = args[1]
            if methodidx not in methods_redir:
                return opcode, args
            
            return opcode, (args[0], methods_redir[methodidx], args[2])
        elif opcode == all_opcodes.OP_CONST_STRING:
            stridx = args[1]
            if stridx not in stridxs_redir:
                return opcode, args

            return opcode, (args[0], stridxs_redir[stridx])
        return opcode, args
    
    new_op_vectors = [redirect(opcode, args) for opcode, args in op_vectors]
    new_insns_blk = encode_opcode_vectors(new_op_vectors) 
    
    code.insns.data = new_insns_blk
    pass


## \brief Make a map for methods from source type ID to ones from desinate.
def make_redir_classes_methods_map(dex_src, typeid_src,
                                   dex_dst, typeid_dst):
    from paraspace.dexfile import DEXFile_linked
    
    methods_src = [(idx, methodid)
                   for idx, methodid in enumerate(dex_src.methodIds.items)
                   if methodid.classIdx == typeid_src]
    
    def make_map_methodid(methodid_src):
        name = DEXFile_linked.get_methodid_name(methodid_src)
        proto = methodid_src.protoIdx
        try:
            methodid_dst = \
                dex_dst.find_methodid_name_proto(name, proto, typeid_dst)
        except ValueError:
            return -1
        methodidx_dst = dex_dst.get_idx_methodid(methodid_dst)
        return methodidx_dst
    
    methods_map = [(methodidx_src, make_map_methodid(methodid_src))
                   for methodidx_src, methodid_src in methods_src]
    methods_map = [(methodidx_src, methodidx_dst)
                   for methodidx_src, methodidx_dst in methods_map
                   if methodidx_dst != -1]
    methods_map = dict(methods_map)
    return methods_map


## \brief Redirect types and methods mentioned in the code of a class.
#
# For code of given class definition, Every mentions of types and
# methods are rewrote to types and methods according typeidxs_redir and
# methods_redir respectively.
#
# \param dex is a DEXFile_linked.
# \param classdef is a class definition.
# \param typeidxs_redir is a map of types.
# \param stridxs_redir is a map of strings.
# \param methods_redir is a map of methods.
#
def class_redirect_indices(dex, classdef, typeidxs_redir, methods_redir,
                           stridxs_redir):
    if not classdef.classDataOffRef.is_true:
        return
    
    classdata = classdef.classDataOffRef.value
    for method in classdata.directMethods.items:
        method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
                                stridxs_redir)
        pass
    for method in classdata.virtualMethods.items:
        method_redirect_indices(dex, method, typeidxs_redir, methods_redir,
                                stridxs_redir)
        pass
    pass


## \brief Make a map to map methods from source types to destinate types.
#
# This function create a map to map methods from source types to
# methods from destinate types in \ref typeidxs_redir.
#
# \param dex_src is a DEXFile_linked that owns source types.
# \param dex_dst is a DEXFile_linked that owns destinate types.
# \param typeidxs_redir is a map of type indices for redirecting types.
# \return a map of method indices.
#
def make_methodidxs_redir_map(dex_src, dex_dst, typeidxs_redir):
    methods_map = {}
    for typeidx_src, typeidx_dst in typeidxs_redir.items():
        typeid_src = dex_src.find_typeid_idx(typeidx_src)
        typeid_dst = dex_dst.find_typeid_idx(typeidx_dst)
        class_methods_map =  make_redir_classes_methods_map(dex_src,
                                                            typeid_src,
                                                            dex_dst,
                                                            typeid_dst)
        methods_map.update(class_methods_map)
        pass
    return methods_map


## \brief Make a map to map string indices from source to destinate DEX.
#
# \param dex_src is soruce of the mapping.
# \param dex_dst is destination of the mapping.
# \param classdefs is _DEX_ClassDefs from dex_src.
# \return a mapping for string indices.
#
def make_stridxs_redir_map(dex_src, dex_dst):
    stridxs_map = {}
    for idx, strid in enumerate(dex_src.stringIds.items):
        try:
            tgt_idx = dex_dst.stringIds.items.index(strid)
        except ValueError:
            continue
        stridxs_map[idx] = tgt_idx
        pass
    return stridxs_map


## \biref Redirect types of all code in given DEXFile_linked.
def dexfile_redirect_indices(dex, typeidxs_redir, methods_redir, stridxs_redir,
                             excludes=set([])):
    for classdef in dex.classDefs.items:
        typeid = classdef.classIdx
        idx = dex.get_idx_typeid(typeid)
        if idx in excludes:
            continue
        class_redirect_indices(dex, classdef, typeidxs_redir, methods_redir,
                               stridxs_redir)
        pass
    pass


## \brief Redirect types for code of types specified by given indices.
def dexfile_redirect_indices_typeidxs(dex, typeidxs_redir, methodidxs_redir,
                                      stridxs_redir, typeidxs):
    typeidxs = set(typeidxs)
    for classdef in dex.classDefs.items:
        typeid = classdef.classIdx
        idx = dex.get_idx_typeid(typeid)
        if idx not in typeidxs:
            continue
        class_redirect_indices(dex, classdef, typeidxs_redir,
                               methodidxs_redir, stridxs_redir)
        pass
    pass


## \brief Collect all type and string indices mentioned in the method code.
#
# \param method is a \ref _DEX_Method.
# \return a list of type indices mentioned in the code.
#
def collect_typeidxs_stridxs_in_method(dex, method):
    from paraspace.dexfile import _DEX_Method, DEXFile_linked
    from paraspace.dalvik_opcodes import decode_insn_blk, all_opcodes
    from itertools import chain
    
    assert isinstance(method, _DEX_Method)

    def get_typeidx_methodidx(methodidx):
        methodid = dex.find_methodid_idx(methodidx)
        method_typeid = methodid.classIdx
        method_typeidx = dex.get_idx_typeid(method_typeid)
        return method_typeidx

    def collect_types_in_op_vector(op_vector):
        code, args = op_vector
        
        if code == all_opcodes.OP_NEW_INSTANCE:
            return args[1]
        
        if code in (all_opcodes.OP_INVOKE_DIRECT,
                    all_opcodes.OP_INVOKE_VIRTUAL,
                    all_opcodes.OP_INVOKE_SUPER,
                    all_opcodes.OP_INVOKE_STATIC,
                    all_opcodes.OP_INVOKE_INTERFACE):
            methodidx = args[2]
            method_typeidx = get_typeidx_methodidx(methodidx)
            return method_typeidx

        if code in (all_opcodes.OP_INVOKE_VIRTUAL_RANGE,
                    all_opcodes.OP_INVOKE_DIRECT_RANGE,
                    all_opcodes.OP_INVOKE_SUPER_RANGE,
                    all_opcodes.OP_INVOKE_STATIC_RANGE,
                    all_opcodes.OP_INVOKE_INTERFACE_RANGE):
            methodidx = args[1]
            method_typeidx = get_typeidx_methodidx(methodidx)
            return method_typeidx

        return None

    def collect_strings_in_op_vector(op_vector):
        code, args = op_vector
        if code == all_opcodes.OP_CONST_STRING:
            stridx = args[1]
            return stridx
        
        return None

    code_blk = DEXFile_linked.get_code_block_method(method)
    op_vectors = decode_insn_blk(code_blk)
    types_insns = [collect_types_in_op_vector(op_vector)
                   for op_vector in op_vectors]
    typeidxs = [idx for idx in types_insns if idx is not None]

    strs_insns = [collect_strings_in_op_vector(op_vectors)
                  for op_vectors in op_vectors]
    stridxs = [idx for idx in strs_insns if idx is not None]
    
    return typeidxs, stridxs


## \brief Collect all type and string indices mentioned by the class code.
def collect_typeidxs_stridxs_mentioned_by_class(dex, classdef):
    from paraspace.dexfile import DEXFile_linked

    assert isinstance(dex, DEXFile_linked)
    
    typeidxs = set()
    stridxs = set()
    methods = DEXFile_linked.get_methods_classdef(classdef)
    for method in methods:
        method_typeidxs, method_stridxs = \
            collect_typeidxs_stridxs_in_method(dex, method)
        typeidxs.update(method_typeidxs)
        stridxs.update(method_stridxs)
        pass
    
    return list(typeidxs), list(stridxs)


## \brief Make a mapping for type indices of injection.
def make_typeidxs_map_after_injection(dex_dst, dex_src,
                                      relative_classdefs,
                                      relative_typeids):
    from paraspace.dexfile import DEXFile_linked
    
    def map_src_dst_typeid(typeid):
        idx_src = dex_src.get_idx_typeid(typeid)
        typename = DEXFile_linked.get_typeid_name(typeid)
        typeid_dst = dex_dst.find_typeid_name(typename)
        idx_dst = dex_dst.get_idx_typeid(typeid_dst)
        dex_dst.find_typeid_idx(idx_dst)
        return idx_src, idx_dst

    def map_src_dst_classdef(classdef):
        typeid = classdef.classIdx
        idx_src, idx_dst = map_src_dst_typeid(typeid)
        return idx_src, idx_dst
    
    typeidxs_classdefs = [map_src_dst_classdef(classdef)
                          for classdef in relative_classdefs]
    typeidxs_typeids = [map_src_dst_typeid(typeid)
                        for typeid in relative_typeids]
    typeidxs_map = dict(typeidxs_classdefs + typeidxs_typeids)
    return typeidxs_map


## \brief Redirect code for methods of injected classes.
def redirect_injected_code(dex_dst, dex_src, classdefs):
    relative_classdefs, relative_typeids, relative_strids = \
        collect_classdefs_relative(dex_src, classdefs)
    
    typeidxs_redir = \
        make_typeidxs_map_after_injection(dex_dst, dex_src, \
                                              relative_classdefs, \
                                              relative_typeids)
    methodidxs_redir = \
        make_methodidxs_redir_map(dex_src, dex_dst, typeidxs_redir)

    stridxs_redir = \
        make_stridxs_redir_map(dex_src, dex_dst)
    
    dexfile_redirect_indices_typeidxs(dex_dst, typeidxs_redir,
                                      methodidxs_redir,
                                      stridxs_redir,
                                      typeidxs_redir.values())
    pass


## \brief Inject classes and relative information to a DEX file.
#
# \param dex_dst is a DEXFile_linked where to inject classes.
# \param dex_src is a DEXFile_linked where the class is from.
# \param classdefs is a list of _DEX_ClassDef instances from \ref dex_src.
# \return a list of _DEX_ClassDef instances for injection.
#
def inject_classdefs(dex_dst, dex_src, classdefs):
    from paraspace.dexfile import DEXFile_linked
    
    assert isinstance(classdefs, (list, tuple))
    assert isinstance(dex_dst, DEXFile_linked)
    assert isinstance(dex_src, DEXFile_linked)

    injected_classdefs, injected_typeids, injected_strids = \
        dexfile_insert_classdefs_relative(dex_dst, dex_src, classdefs)
    redirect_injected_code(dex_dst, dex_src, classdefs)
    return injected_classdefs


## \brief Redirect all references of given class and its methods.
#
# Redirect all references of given class and its methods to target class.
#
def redirect_type(dex, src_classname, tgt_classname, exclude_classnames):
    src_typeid = dex.find_typeid_name(src_classname)
    src_typeidx = dex.get_idx_typeid(src_typeid)
    tgt_typeid = dex.find_typeid_name(tgt_classname)
    tgt_typeidx = dex.get_idx_typeid(tgt_typeid)
    typeidxs_redir = {src_typeidx: tgt_typeidx}
    
    methodidxs_redir = \
        make_methodidxs_redir_map(dex, dex, typeidxs_redir)

    stridxs_redir = {}
    
    ex_typeids = [dex.find_typeid_name(name)
                   for name in exclude_classnames]
    ex_typeidxs = [dex.get_idx_typeid(typeid)
                   for typeid in ex_typeids]
    dexfile_redirect_indices(dex, typeidxs_redir, methodidxs_redir,
                             stridxs_redir, excludes=ex_typeidxs)
    pass


## \brief Inject and redirect a _DEX_ClassDef from one linked to another.
#
# The _DEX_ClassDef given by inj_classname would be inserted to dst_linked,
# and redirect all invoking of type, given by redir_classname, to
# the injected one.
#
def inject_redir_no_restore(src_linked, inj_classname,
                            dst_linked, redir_classname, decls):
    inj_classdef = src_linked.find_class_name(inj_classname)
    injected_classdefs = inject_classdefs(dst_linked, src_linked,
                                          [inj_classdef])
    excludes = [dst_linked.get_classdef_name(classdef)
                for classdef in injected_classdefs]
    redirect_type(dst_linked, redir_classname, inj_classname, excludes)
    pass


## \brief Make indices map for typeid and methodid.
#
# It is used to create indices maps for typeid and methodid after
# changing order.  For example, after sorting sorted array after an
# injection, this function create maps for remapping indices mentioned
# in the code.
# 
def _make_idx_map(saved_typeids, saved_methodids, saved_strids,
                  new_typeids, new_methodids, new_strids):
    methodidxs_map = dict([(idx, new_methodids.index(methodid))
                           for idx, methodid in enumerate(saved_methodids)])
    typeidxs_map = dict([(idx, new_typeids.index(typeid))
                         for idx, typeid in enumerate(saved_typeids)])
    stridxs_map = dict([(idx, new_strids.index(strid))
                        for idx, strid in enumerate(saved_strids)])
    
    return typeidxs_map, methodidxs_map, stridxs_map


## \brief Sort sorted arrays and remapping indices for code blocks.
#
# Since sorting changes the order of sorted arrays, code blocks should
# be updated by remapping indices, typeid indices and methodid indices.
#
def dex_sort_sorted_arrays_consistent(dex_linked):
    from paraspace.dex_deptracker import dex_sort_sorted_arrays
    
    saved_typeids = list(dex_linked.typeIds.items)
    saved_methodids = list(dex_linked.methodIds.items)
    saved_strids = list(dex_linked.stringIds.items)
    
    dex_sort_sorted_arrays(dex_linked)

    new_typeids = dex_linked.typeIds.items
    new_methodids = dex_linked.methodIds.items
    new_strids = dex_linked.stringIds.items
    typeidxs_map, methodidxs_map, stridxs_map = \
        _make_idx_map(saved_typeids, saved_methodids, saved_strids, \
                          new_typeids, new_methodids, new_strids)
    
    dexfile_redirect_indices(dex_linked, typeidxs_map, methodidxs_map,
                             stridxs_map)
    pass


## \brief Inject and redirect a _DEX_ClassDef from one linked to another.
#
# The _DEX_ClassDef given by inj_classname would be inserted to dst_linked,
# and redirect all invoking of type, given by redir_classname, to
# the injected one.
#
def inject_redir(src_linked, inj_classname,
                 dst_linked, redir_classname, decls):
    from paraspace.dex_deptracker import restore_dependencies
    
    inject_redir_no_restore(src_linked, inj_classname,
                            dst_linked, redir_classname, decls)
    dex_sort_sorted_arrays_consistent(dst_linked)
    restore_dependencies(dst_linked, decls)
    pass