| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2008 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | /* |
| 18 | * Dalvik bytecode verifier. |
| 19 | */ |
| 20 | #ifndef _DALVIK_CODEVERIFY |
| 21 | #define _DALVIK_CODEVERIFY |
| 22 | |
| 23 | #include "analysis/VerifySubs.h" |
| 24 | |
| 25 | |
| 26 | /* |
| 27 | * Enumeration for register type values. The "hi" piece of a 64-bit value |
| 28 | * MUST immediately follow the "lo" piece in the enumeration, so we can check |
| 29 | * that hi==lo+1. |
| 30 | * |
| 31 | * Assignment of constants: |
| 32 | * [-MAXINT,-32768) : integer |
| 33 | * [-32768,-128) : short |
| 34 | * [-128,0) : byte |
| 35 | * 0 : zero |
| 36 | * 1 : one |
| 37 | * [2,128) : posbyte |
| 38 | * [128,32768) : posshort |
| 39 | * [32768,65536) : char |
| 40 | * [65536,MAXINT] : integer |
| 41 | * |
| 42 | * Allowed "implicit" widening conversions: |
| 43 | * zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null) |
| 44 | * one -> boolean, posbyte, byte, posshort, short, char, integer |
| 45 | * boolean -> posbyte, byte, posshort, short, char, integer |
| 46 | * posbyte -> posshort, short, integer, char |
| 47 | * byte -> short, integer |
| 48 | * posshort -> integer, char |
| 49 | * short -> integer |
| 50 | * char -> integer |
| 51 | * |
| 52 | * In addition, all of the above can convert to "float". |
| 53 | * |
| 54 | * We're more careful with integer values than the spec requires. The |
| 55 | * motivation is to restrict byte/char/short to the correct range of values. |
| 56 | * For example, if a method takes a byte argument, we don't want to allow |
| 57 | * the code to load the constant "1024" and pass it in. |
| 58 | */ |
| 59 | enum { |
| 60 | kRegTypeUnknown = 0, /* initial state; use value=0 so calloc works */ |
| 61 | kRegTypeUninit = 1, /* MUST be odd to distinguish from pointer */ |
| 62 | kRegTypeConflict, /* merge clash makes this reg's type unknowable */ |
| 63 | |
| 64 | /* |
| 65 | * Category-1nr types. The order of these is chiseled into a couple |
| 66 | * of tables, so don't add, remove, or reorder if you can avoid it. |
| 67 | */ |
| 68 | #define kRegType1nrSTART kRegTypeFloat |
| 69 | kRegTypeFloat, |
| 70 | kRegTypeZero, /* 32-bit 0, could be Boolean, Int, Float, or Ref */ |
| 71 | kRegTypeOne, /* 32-bit 1, could be Boolean, Int, Float */ |
| 72 | kRegTypeBoolean, /* must be 0 or 1 */ |
| 73 | kRegTypePosByte, /* byte, known positive (can become char) */ |
| 74 | kRegTypeByte, |
| 75 | kRegTypePosShort, /* short, known positive (can become char) */ |
| 76 | kRegTypeShort, |
| 77 | kRegTypeChar, |
| 78 | kRegTypeInteger, |
| 79 | #define kRegType1nrEND kRegTypeInteger |
| 80 | |
| 81 | kRegTypeLongLo, /* lower-numbered register; endian-independent */ |
| 82 | kRegTypeLongHi, |
| 83 | kRegTypeDoubleLo, |
| 84 | kRegTypeDoubleHi, |
| 85 | |
| 86 | /* |
| 87 | * Enumeration max; this is used with "full" (32-bit) RegType values. |
| 88 | * |
| 89 | * Anything larger than this is a ClassObject or uninit ref. Mask off |
| 90 | * all but the low 8 bits; if you're left with kRegTypeUninit, pull |
| 91 | * the uninit index out of the high 24. Because kRegTypeUninit has an |
| 92 | * odd value, there is no risk of a particular ClassObject pointer bit |
| 93 | * pattern being confused for it (assuming our class object allocator |
| 94 | * uses word alignment). |
| 95 | */ |
| 96 | kRegTypeMAX |
| 97 | }; |
| 98 | #define kRegTypeUninitMask 0xff |
| 99 | #define kRegTypeUninitShift 8 |
| 100 | |
| 101 | /* |
| 102 | * RegType holds information about the type of data held in a register. |
| 103 | * For most types it's a simple enum. For reference types it holds a |
| 104 | * pointer to the ClassObject, and for uninitialized references it holds |
| 105 | * an index into the UninitInstanceMap. |
| 106 | */ |
| 107 | typedef u4 RegType; |
| 108 | |
| 109 | /* table with merge logic for primitive types */ |
| 110 | extern const char gDvmMergeTab[kRegTypeMAX][kRegTypeMAX]; |
| 111 | |
| 112 | |
| 113 | /* |
| 114 | * Returns "true" if the flags indicate that this address holds the start |
| 115 | * of an instruction. |
| 116 | */ |
| 117 | INLINE bool dvmInsnIsOpcode(const InsnFlags* insnFlags, int addr) { |
| 118 | return (insnFlags[addr] & kInsnFlagWidthMask) != 0; |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 | * Extract the unsigned 16-bit instruction width from "flags". |
| 123 | */ |
| 124 | INLINE int dvmInsnGetWidth(const InsnFlags* insnFlags, int addr) { |
| 125 | return insnFlags[addr] & kInsnFlagWidthMask; |
| 126 | } |
| 127 | |
| 128 | /* |
| 129 | * Changed? |
| 130 | */ |
| 131 | INLINE bool dvmInsnIsChanged(const InsnFlags* insnFlags, int addr) { |
| 132 | return (insnFlags[addr] & kInsnFlagChanged) != 0; |
| 133 | } |
| 134 | INLINE void dvmInsnSetChanged(InsnFlags* insnFlags, int addr, bool changed) |
| 135 | { |
| 136 | if (changed) |
| 137 | insnFlags[addr] |= kInsnFlagChanged; |
| 138 | else |
| 139 | insnFlags[addr] &= ~kInsnFlagChanged; |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | * Visited? |
| 144 | */ |
| 145 | INLINE bool dvmInsnIsVisited(const InsnFlags* insnFlags, int addr) { |
| 146 | return (insnFlags[addr] & kInsnFlagVisited) != 0; |
| 147 | } |
| 148 | INLINE void dvmInsnSetVisited(InsnFlags* insnFlags, int addr, bool changed) |
| 149 | { |
| 150 | if (changed) |
| 151 | insnFlags[addr] |= kInsnFlagVisited; |
| 152 | else |
| 153 | insnFlags[addr] &= ~kInsnFlagVisited; |
| 154 | } |
| 155 | |
| 156 | /* |
| 157 | * Visited or changed? |
| 158 | */ |
| 159 | INLINE bool dvmInsnIsVisitedOrChanged(const InsnFlags* insnFlags, int addr) { |
| 160 | return (insnFlags[addr] & (kInsnFlagVisited|kInsnFlagChanged)) != 0; |
| 161 | } |
| 162 | |
| 163 | /* |
| 164 | * In a "try" block? |
| 165 | */ |
| 166 | INLINE bool dvmInsnIsInTry(const InsnFlags* insnFlags, int addr) { |
| 167 | return (insnFlags[addr] & kInsnFlagInTry) != 0; |
| 168 | } |
| 169 | INLINE void dvmInsnSetInTry(InsnFlags* insnFlags, int addr, bool inTry) |
| 170 | { |
| 171 | assert(inTry); |
| 172 | //if (inTry) |
| 173 | insnFlags[addr] |= kInsnFlagInTry; |
| 174 | //else |
| 175 | // insnFlags[addr] &= ~kInsnFlagInTry; |
| 176 | } |
| 177 | |
| 178 | /* |
| 179 | * Instruction is a branch target or exception handler? |
| 180 | */ |
| 181 | INLINE bool dvmInsnIsBranchTarget(const InsnFlags* insnFlags, int addr) { |
| 182 | return (insnFlags[addr] & kInsnFlagBranchTarget) != 0; |
| 183 | } |
| 184 | INLINE void dvmInsnSetBranchTarget(InsnFlags* insnFlags, int addr, |
| 185 | bool isBranch) |
| 186 | { |
| 187 | assert(isBranch); |
| 188 | //if (isBranch) |
| 189 | insnFlags[addr] |= kInsnFlagBranchTarget; |
| 190 | //else |
| 191 | // insnFlags[addr] &= ~kInsnFlagBranchTarget; |
| 192 | } |
| 193 | |
| 194 | /* |
| 195 | * Instruction is a GC point? |
| 196 | */ |
| 197 | INLINE bool dvmInsnIsGcPoint(const InsnFlags* insnFlags, int addr) { |
| 198 | return (insnFlags[addr] & kInsnFlagGcPoint) != 0; |
| 199 | } |
| 200 | INLINE void dvmInsnSetGcPoint(InsnFlags* insnFlags, int addr, |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 201 | bool isGcPoint) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 202 | { |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 203 | assert(isGcPoint); |
| 204 | //if (isGcPoint) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 205 | insnFlags[addr] |= kInsnFlagGcPoint; |
| 206 | //else |
| 207 | // insnFlags[addr] &= ~kInsnFlagGcPoint; |
| 208 | } |
| 209 | |
| 210 | |
| 211 | /* |
| 212 | * Table that maps uninitialized instances to classes, based on the |
| 213 | * address of the new-instance instruction. |
| 214 | */ |
| 215 | typedef struct UninitInstanceMap { |
| 216 | int numEntries; |
| 217 | struct { |
| 218 | int addr; /* code offset, or -1 for method arg ("this") */ |
| 219 | ClassObject* clazz; /* class created at this address */ |
| 220 | } map[1]; |
| 221 | } UninitInstanceMap; |
| 222 | #define kUninitThisArgAddr (-1) |
| 223 | #define kUninitThisArgSlot 0 |
| 224 | |
| 225 | /* |
| 226 | * Create a new UninitInstanceMap. |
| 227 | */ |
| 228 | UninitInstanceMap* dvmCreateUninitInstanceMap(const Method* meth, |
| 229 | const InsnFlags* insnFlags, int newInstanceCount); |
| 230 | |
| 231 | /* |
| 232 | * Release the storage associated with an UninitInstanceMap. |
| 233 | */ |
| 234 | void dvmFreeUninitInstanceMap(UninitInstanceMap* uninitMap); |
| 235 | |
| 236 | /* |
| 237 | * Associate a class with an address. Returns the map slot index, or -1 |
| 238 | * if the address isn't listed in the map (shouldn't happen) or if a |
| 239 | * different class is already associated with the address (shouldn't |
| 240 | * happen either). |
| 241 | */ |
| 242 | //int dvmSetUninitInstance(UninitInstanceMap* uninitMap, int addr, |
| 243 | // ClassObject* clazz); |
| 244 | |
| 245 | /* |
| 246 | * Return the class associated with an uninitialized reference. Pass in |
| 247 | * the map index. |
| 248 | */ |
| 249 | //ClassObject* dvmGetUninitInstance(const UninitInstanceMap* uninitMap, int idx); |
| 250 | |
| 251 | /* |
| 252 | * Clear the class associated with an uninitialized reference. Pass in |
| 253 | * the map index. |
| 254 | */ |
| 255 | //void dvmClearUninitInstance(UninitInstanceMap* uninitMap, int idx); |
| 256 | |
| 257 | |
| 258 | /* |
| 259 | * Verify bytecode in "meth". "insnFlags" should be populated with |
| 260 | * instruction widths and "in try" flags. |
| 261 | */ |
| 262 | bool dvmVerifyCodeFlow(const Method* meth, InsnFlags* insnFlags, |
| 263 | UninitInstanceMap* uninitMap); |
| 264 | |
| 265 | #endif /*_DALVIK_CODEVERIFY*/ |