From 8a116bba989dbf4c786ffb1390fe764247f5fb6e Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Mon, 11 May 2009 00:40:03 +0000 Subject: [PATCH] Changed the implementation details for the sparse-switch and packed-switch pseudo opcodes to use an int[], instead of a List. Also added additional validity checking to SparseSwitchData.make() git-svn-id: https://smali.googlecode.com/svn/trunk@36 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../antlr3/org/JesusFreke/smali/smaliParser.g | 34 +++++++++--- .../org/JesusFreke/smali/smaliTreeWalker.g | 45 +++++++++++----- .../dexlib/code/Format/PackedSwitchData.java | 11 ++-- .../dexlib/code/Format/SparseSwitchData.java | 54 ++++++++++++------- 4 files changed, 98 insertions(+), 46 deletions(-) diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g index 8116f8a1..e60d2aac 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliParser.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliParser.g @@ -54,9 +54,11 @@ tokens { I_ARRAY_ELEMENTS; I_PACKED_SWITCH_START_KEY; I_PACKED_SWITCH_BASE_OFFSET; + I_PACKED_SWITCH_TARGET_COUNT; I_PACKED_SWITCH_TARGETS; I_SPARSE_SWITCH_BASE_OFFSET; I_SPARSE_SWITCH_KEYS; + I_SPARSE_SWITCH_TARGET_COUNT; I_SPARSE_SWITCH_TARGETS; I_STATEMENTS; I_STATEMENT_FORMAT10t; @@ -261,6 +263,7 @@ instruction returns [int size] | PACKED_SWITCH_DIRECTIVE { + int targetCount = 0; if (($statements::currentAddress \% 2) != 0) { needsNop = true; $size = 2; @@ -273,18 +276,28 @@ instruction returns [int size] fixed_32bit_literal - (switch_target += offset_or_label {$size+=4;})* + (switch_target += offset_or_label {$size+=4; targetCount++;})* END_PACKED_SWITCH_DIRECTIVE {$size = $size + 8;} /*add a nop statement before this if needed to force the correct alignment*/ -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) - ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET $base_offset) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS $switch_target*)) - -> ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET $base_offset) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS $switch_target*)) + ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] + ^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset) + ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) + ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) + ) + + -> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] + ^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset) + ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) + ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) + ) | SPARSE_SWITCH_DIRECTIVE { + int targetCount = 0; if (($statements::currentAddress \% 2) != 0) { needsNop = true; $size = 2; @@ -295,15 +308,22 @@ instruction returns [int size] base_offset = offset_or_label - (fixed_32bit_literal switch_target += offset_or_label {$size += 8;})* + (fixed_32bit_literal switch_target += offset_or_label {$size += 8; targetCount++;})* END_SPARSE_SWITCH_DIRECTIVE {$size = $size + 4;} /*add a nop statement before this if needed to force the correct alignment*/ -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) - ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET $base_offset) ^(I_SPARSE_SWITCH_KEYS fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) - -> ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET $base_offset) ^(I_SPARSE_SWITCH_KEYS fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) - + ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] + ^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset) + I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] + ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) + ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) + -> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] + ^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset) + I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] + ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) + ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) ; offset_or_label diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index ccef3970..e98bb409 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -212,31 +212,50 @@ array_elements returns[List values] { $values.add($fixed_size_literal.value); })*); + +packed_switch_target_count returns[int targetCount] + : I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);}; -packed_switch_targets[int baseOffset] returns[List targets] - : {$targets = new ArrayList();} +packed_switch_targets[int baseOffset] returns[int[\] targets] + : ^(I_PACKED_SWITCH_TARGETS + packed_switch_target_count + { + int targetCount = $packed_switch_target_count.targetCount; + $targets = new int[targetCount]; + int targetsPosition = 0; + } + (offset_or_label { - $targets.add($offset_or_label.offsetValue - $baseOffset); + targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; })* ); + +sparse_switch_target_count returns[int targetCount] + : I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);}; -sparse_switch_keys returns[List keys] - : {$keys = new ArrayList();} +sparse_switch_keys[int targetCount] returns[int[\] keys] + : { + $keys = new int[$targetCount]; + int keysPosition = 0; + } ^(I_SPARSE_SWITCH_KEYS (fixed_32bit_literal { - $keys.add($fixed_32bit_literal.value); + $keys[keysPosition++] = $fixed_32bit_literal.value; })* ); -sparse_switch_targets[int baseOffset] returns[List targets] - : {$targets = new ArrayList();} +sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] + : { + $targets = new int[$targetCount]; + int targetsPosition = 0; + } ^(I_SPARSE_SWITCH_TARGETS (offset_or_label { - $targets.add($offset_or_label.offsetValue - $baseOffset); + $targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; })* ); @@ -703,15 +722,15 @@ instruction returns[Instruction instruction] ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET base_offset=offset_or_label) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_targets[$base_offset.offsetValue]) { int startKey = $fixed_32bit_literal.value; - List targets = $packed_switch_targets.targets; + int[] targets = $packed_switch_targets.targets; $instruction = PackedSwitchData.make(dexFile, startKey, targets); } | - ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_keys sparse_switch_targets[$base_offset.offsetValue]) + ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] sparse_switch_targets[$base_offset.offsetValue, $sparse_switch_target_count.targetCount]) { - List keys = $sparse_switch_keys.keys; - List targets = $sparse_switch_targets.targets; + int[] keys = $sparse_switch_keys.keys; + int[] targets = $sparse_switch_targets.targets; $instruction = SparseSwitchData.make(dexFile, keys, targets); } diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/PackedSwitchData.java b/src/main/java/org/JesusFreke/dexlib/code/Format/PackedSwitchData.java index 1831144f..9dc27ad1 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Format/PackedSwitchData.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/PackedSwitchData.java @@ -36,16 +36,15 @@ import java.util.List; public class PackedSwitchData { - //TODO: switch from List to int[] - public static Instruction make(DexFile dexFile, int firstKey, List targets) { + public static Instruction make(DexFile dexFile, int firstKey, int[] targets) { byte[] bytes; - if (targets.size() > 0xFFFF) { + if (targets.length > 0xFFFF) { throw new RuntimeException("The packed-switch data contains too many elements. " + "The maximum number of switch elements is 65535"); } - bytes = new byte[targets.size() * 4 + 8]; + bytes = new byte[targets.length * 4 + 8]; int position = 8; for (int target: targets) { @@ -59,8 +58,8 @@ public class PackedSwitchData bytes[0] = 0x00; bytes[1] = 0x01; - bytes[2] = (byte)targets.size(); - bytes[3] = (byte)(targets.size() >> 8); + bytes[2] = (byte)targets.length; + bytes[3] = (byte)(targets.length >> 8); bytes[4] = (byte)firstKey; bytes[5] = (byte)(firstKey >> 8); diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/SparseSwitchData.java b/src/main/java/org/JesusFreke/dexlib/code/Format/SparseSwitchData.java index e73050c1..e38a0815 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Format/SparseSwitchData.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/SparseSwitchData.java @@ -36,46 +36,60 @@ import java.util.List; public class SparseSwitchData { - //TODO: change from 2 List to int[,] - public static Instruction make(DexFile dexFile, List keys, List targets) { + public static Instruction make(DexFile dexFile, int[] keys, int[] targets) { byte[] bytes; - if (keys.size() != targets.size()) { + if (keys.length != targets.length) { throw new RuntimeException("The number of keys and offsets don't match"); } + if (targets.length == 0) { + throw new RuntimeException("The sparse-switch data must contain at least 1 key/target"); + } - if (targets.size() > 0xFFFF) { + if (targets.length > 0xFFFF) { throw new RuntimeException("The sparse-switch data contains too many elements. " + "The maximum number of switch elements is 65535"); } - //TODO: need to sort the switch elements based on key, in ascending order + bytes = new byte[targets.length * 8 + 4]; + int position = 8; - bytes = new byte[targets.size() * 8 + 4]; - int position = 4; + //TODO: should we throw an error if there are no switch elements? + if (targets.length > 0) { + int key = keys[0]; + bytes[4] = (byte)key; + bytes[5] = (byte)(key >> 8); + bytes[6] = (byte)(key >> 16); + bytes[7] = (byte)(key >> 24); + for (int i=1; i> 8); - bytes[position++] = (byte)(key >> 16); - bytes[position++] = (byte)(key >> 24); - } + bytes[position++] = (byte)key; + bytes[position++] = (byte)(key >> 8); + bytes[position++] = (byte)(key >> 16); + bytes[position++] = (byte)(key >> 24); + } - for (int target: targets) { - bytes[position++] = (byte)target; - bytes[position++] = (byte)(target >> 8); - bytes[position++] = (byte)(target >> 16); - bytes[position++] = (byte)(target >> 24); + for (int target: targets) { + bytes[position++] = (byte)target; + bytes[position++] = (byte)(target >> 8); + bytes[position++] = (byte)(target >> 16); + bytes[position++] = (byte)(target >> 24); + } } //sparse-switch psuedo-opcode bytes[0] = 0x00; bytes[1] = 0x02; - bytes[2] = (byte)targets.size(); - bytes[3] = (byte)(targets.size() >> 8); + bytes[2] = (byte)targets.length; + bytes[3] = (byte)(targets.length >> 8); return new Instruction(dexFile, bytes, null); }