Source code for apkutils.dex.jvm.optimization.jumps

# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import struct

from .. import ir
from ..jvmops import *

def _calcMinimumPositions(instrs):
    posd = {}
    pos = 0
    for ins in instrs:
        posd[ins] = pos
        if isinstance(ins, ir.LazyJumpBase):
            pos += ins.min
        elif isinstance(ins, ir.Switch):
            pad = (-pos-1) % 4
            pos += pad + ins.nopad_size
        else:
            pos += len(ins.bytecode)
    return posd, pos

[docs]def optimizeJumps(irdata): # For jump offsets of more than +-32767, a longer form of the jump instruction # is required. This function finds the optimal jump widths by optimistically # starting with everything narrow and then iteratively marking instructions # as wide if their offset is too large (in rare cases, this can in turn cause # other jumps to become wide, hence iterating until convergence) instrs = irdata.flat_instructions jump_instrs = [ins for ins in instrs if isinstance(ins, ir.LazyJumpBase)] while 1: done = True posd, _ = _calcMinimumPositions(instrs) for ins in jump_instrs: if ins.min < ins.max and ins.widenIfNecessary(irdata.labels, posd): done = False if done: break for ins in jump_instrs: assert ins.min <= ins.max ins.max = ins.min
[docs]def createBytecode(irdata): instrs = irdata.flat_instructions posd, end_pos = _calcMinimumPositions(instrs) bytecode = bytearray() for ins in instrs: if isinstance(ins, (ir.LazyJumpBase, ir.Switch)): ins.calcBytecode(posd, irdata.labels) bytecode += ins.bytecode assert len(bytecode) == end_pos prev_instr_map = dict(zip(instrs[1:], instrs)) packed_excepts = [] for s, e, h, c in irdata.excepts: # There appears to be a bug in the JVM where in rare cases, it throws # the exception at the address of the instruction _before_ the instruction # that actually caused the exception, triggering the wrong handler # therefore we include the previous (IR) instruction too # Note that this cannot cause an overlap because in that case the previous # instruction would just be a label and hence not change anything s = prev_instr_map.get(s, s) s_off = posd[s] e_off = posd[e] h_off = posd[h] assert s_off <= e_off if s_off < e_off: packed_excepts.append(struct.pack('>HHHH', s_off, e_off, h_off, c)) else: print('Skipping zero width exception!') assert 0 return bytes(bytecode), packed_excepts