diff options
author | Anthony Green <green@gcc.gnu.org> | 1998-09-06 15:36:06 +0000 |
---|---|---|
committer | Anthony Green <green@gcc.gnu.org> | 1998-09-06 15:36:06 +0000 |
commit | e04a16fbeee9504247e0d069171a627096225028 (patch) | |
tree | b78f906318225a5e7bd3471f008be772727bdcea /gcc/java/verify.c | |
parent | fc08e32958a1df77493bfe4abc52f88c7ade6251 (diff) | |
download | gcc-e04a16fbeee9504247e0d069171a627096225028.zip gcc-e04a16fbeee9504247e0d069171a627096225028.tar.gz gcc-e04a16fbeee9504247e0d069171a627096225028.tar.bz2 |
Initial revision
From-SVN: r22299
Diffstat (limited to 'gcc/java/verify.c')
-rw-r--r-- | gcc/java/verify.c | 1239 |
1 files changed, 1239 insertions, 0 deletions
diff --git a/gcc/java/verify.c b/gcc/java/verify.c new file mode 100644 index 0000000..82a081f --- /dev/null +++ b/gcc/java/verify.c @@ -0,0 +1,1239 @@ +/* Handle verification of bytecoded methods for the GNU compiler for + the Java(TM) language. + Copyright (C) 1997 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + +Java and all Java-based marks are trademarks or registered trademarks +of Sun Microsystems, Inc. in the United States and other countries. +The Free Software Foundation is independent of Sun Microsystems, Inc. */ + +#include <stdio.h> +#include "config.h" +#include "tree.h" +#include "java-tree.h" +#include "javaop.h" +#include "java-opcodes.h" +#include "jcf.h" +#include "java-except.h" + +extern int stack_pointer; + +/* During verification, start of the current subroutine (jsr target). */ +tree current_subr; + +/* A list of pending blocks, chained using LABEL_PENDING_CHAIN. + A pending block is one that has LABEL_CHANGED set, which means + it requires (re-) verification. */ +tree pending_blocks; + +/* Append TARGET_LABEL to the pending_block stack unless already in it. */ + +void +push_pending_label (target_label) + tree target_label; +{ + if (! LABEL_CHANGED (target_label)) + { + LABEL_PENDING_CHAIN (target_label) = pending_blocks; + pending_blocks = target_label; + LABEL_CHANGED (target_label) = 1; + } +} + +/* Note that TARGET_LABEL is a possible successor instruction. + Merge the type state etc. + Return NULL on sucess, or an error message on failure. */ + +static char * +check_pending_block (target_label) + tree target_label; +{ + int changed = merge_type_state (target_label); + + if (changed) + { + if (changed < 0) + return "types could not be merged"; + push_pending_label (target_label); + } + + if (current_subr == NULL) + { + if (LABEL_IN_SUBR (target_label)) + return "might transfer control into subroutine"; + } + else + { + if (LABEL_IN_SUBR (target_label)) + { + if (LABEL_SUBR_START (target_label) != current_subr) + return "transfer out of subroutine"; + } + else if (! LABEL_VERIFIED (target_label)) + { + LABEL_IN_SUBR (target_label) = 1; + LABEL_SUBR_START (target_label) = current_subr; + } + else + return "transfer out of subroutine"; + } + return NULL; +} + +/* Return the "merged" types of TYPE1 and TYPE2. + If either is primitive, the other must match (after promotion to int). + For reference types, return the common super-class. + Return TYPE_UNKNOWN if the types cannot be merged. */ + +tree +merge_types (type1, type2) + tree type1, type2; +{ + if (type1 == type2) + return type1; + if (type1 == TYPE_UNKNOWN || type2 == TYPE_UNKNOWN + || type1 == TYPE_RETURN_ADDR || type2 == TYPE_RETURN_ADDR) + return TYPE_UNKNOWN; + if (TREE_CODE (type1) == POINTER_TYPE && TREE_CODE (type2) == POINTER_TYPE) + { + int depth1, depth2; + tree tt1, tt2; + /* ptr_type_node is only used for a null reference, + which is compatible with any reference type. */ + if (type1 == ptr_type_node || type2 == object_ptr_type_node) + return type2; + if (type2 == ptr_type_node || type1 == object_ptr_type_node) + return type1; + + tt1 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type1)); + tt2 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type2)); + + if (TYPE_ARRAY_P (tt1) || TYPE_ARRAY_P (tt2)) + { + if (TYPE_ARRAY_P (tt1) == TYPE_ARRAY_P (tt2)) + { + tree el_type1 = TYPE_ARRAY_ELEMENT (tt1); + tree el_type2 = TYPE_ARRAY_ELEMENT (tt2); + tree el_type = NULL_TREE; + if (el_type1 == el_type2) + el_type = el_type1; + else if (TREE_CODE (el_type1) == POINTER_TYPE + && TREE_CODE (el_type2) == POINTER_TYPE) + el_type = merge_types (el_type1, el_type2); + if (el_type != NULL_TREE) + { + HOST_WIDE_INT len1 = java_array_type_length (tt1); + HOST_WIDE_INT len2 = java_array_type_length (tt2); + if (len1 != len2) + len1 = -1; + else if (el_type1 == el_type2) + return type1; + return promote_type (build_java_array_type (el_type, len1)); + } + } + return object_ptr_type_node; + } + type1 = tt1; + type2 = tt2; + + depth1 = class_depth (type1); + depth2 = class_depth (type2); + for ( ; depth1 > depth2; depth1--) + type1 = TYPE_BINFO_BASETYPE (type1, 0); + for ( ; depth2 > depth1; depth2--) + type2 = TYPE_BINFO_BASETYPE (type2, 0); + while (type1 != type2) + { + type1 = TYPE_BINFO_BASETYPE (type1, 0); + type2 = TYPE_BINFO_BASETYPE (type2, 0); + } + return promote_type (type1); + } + if (INTEGRAL_TYPE_P (type1) && INTEGRAL_TYPE_P (type2) + && TYPE_PRECISION (type1) <= 32 && TYPE_PRECISION (type2) <= 32) + return int_type_node; + return TYPE_UNKNOWN; +} + +/* Merge the current type state with that at LABEL. + Return -1 the the states are incompatible (i.e. on error), + 0 if there was no change, and 1 if there was a change. */ + +int +merge_type_state (label) + tree label; +{ + int nlocals = DECL_MAX_LOCALS(current_function_decl); + int cur_length = stack_pointer + nlocals; + tree vec = LABEL_TYPE_STATE (label); + tree return_map; + if (vec == NULL_TREE) + { + vec = make_tree_vec (cur_length); + LABEL_TYPE_STATE (label) = vec; + while (--cur_length >= 0) + TREE_VEC_ELT (vec, cur_length) = type_map [cur_length]; + return 1; + } + else + { + int i; + int changed = 0; + if (LABEL_IS_SUBR_START (label) && LABEL_VERIFIED (label) + && current_subr != label) + return_map = LABEL_RETURN_TYPE_STATE (label); + else + return_map = NULL_TREE; + if (TREE_VEC_LENGTH (vec) != cur_length) + { + return -1; + } + for (i = 0; i < cur_length; i++) + { + tree old_type = TREE_VEC_ELT (vec, i); + tree new_type = merge_types (old_type, type_map [i]); + if (TREE_VEC_ELT (vec, i) != new_type) + { + /* If there has been a change, note that since we must re-verify. + However, if the label is the start of a subroutine, + we don't care about local variables that are neither + set nor used in the sub-routine. */ + if (return_map == NULL_TREE || i >= nlocals + || TREE_VEC_ELT (return_map, i) != TYPE_UNUSED + || (TYPE_IS_WIDE (new_type) + && TREE_VEC_ELT (return_map, i+1) != TYPE_UNUSED)) + changed = 1; + } + TREE_VEC_ELT (vec, i) = new_type; + if (new_type == TYPE_UNKNOWN) + { + if (i >= nlocals) + return -1; + } + else if (TYPE_IS_WIDE (new_type)) + i++; + } + return changed; + } +} + +/* Handle dup-like operations. */ + +static void +type_stack_dup (size, offset) + int size, offset; +{ + tree type[4]; + int index; + if (size + offset > stack_pointer) + error ("stack underflow - dup* operation"); + for (index = 0; index < size + offset; index++) + { + type[index] = stack_type_map[stack_pointer - 1]; + if (type[index] == void_type_node) + { + index++; + type[index] = stack_type_map[stack_pointer - 2]; + if (! TYPE_IS_WIDE (type[index])) + fatal ("internal error - dup operation"); + if (index == size || index == size + offset) + fatal ("dup operation splits 64-bit number"); + } + pop_type (type[index]); + } + for (index = size; --index >= 0; ) + { + if (type[index] != void_type_node) + push_type (type[index]); + } + + for (index = size + offset; --index >= 0; ) + { + if (type[index] != void_type_node) + push_type (type[index]); + } +} + +/* This causes the next iteration to ignore the next instruction + and look for some other unhandled instruction. */ +#define INVALIDATE_PC (prevpc = -1, oldpc = PC, PC = INVALID_PC) +#define INVALID_PC (-1) + +#define VERIFICATION_ERROR(MESSAGE) \ + do { message = MESSAGE; goto verify_error; } while (0) + +#define PUSH_PENDING(LABEL) \ + do { if ((message = check_pending_block (LABEL)) != NULL) \ + goto verify_error; } while (0) + +#ifdef __GNUC__ +#define CHECK_PC_IN_RANGE(PC) ({if (PC < 0 || PC > length) goto bad_pc; 1;}) +#else +#define CHECK_PC_IN_RANGE(PC) (PC < 0 || PC > length ? \ + (fatal("Bad byte codes.\n"), 0) : 1) +#endif + +#define BCODE byte_ops + +/* Verify the bytecodes of the current method. + Return 1 on sucess, 0 on failure. */ +int +verify_jvm_instructions (jcf, byte_ops, length) + JCF* jcf; + unsigned char* byte_ops; + long length; +{ + tree label; + int wide = 0; + int op_code; + int PC; + int oldpc; /* PC of start of instruction. */ + int prevpc; /* If >= 0, PC of previous instruction. */ + char *message; + int i; + register unsigned char *p; + struct eh_range *prev_eh_ranges = NULL_EH_RANGE; + struct eh_range *eh_ranges; + + jint int_value = -1; + + pending_blocks = NULL_TREE; + + /* Handle the exception table. */ + method_init_exceptions (); + JCF_SEEK (jcf, DECL_CODE_OFFSET (current_function_decl) + length); + i = JCF_readu2 (jcf); + + /* We read the exception backwards. */ + p = jcf->read_ptr + 8 * i; + while (--i >= 0) + { + int start_pc = GET_u2 (p-8); + int end_pc = GET_u2 (p-6); + int handler_pc = GET_u2 (p-4); + int catch_type = GET_u2 (p-2); + p -= 8; + + if (start_pc < 0 || start_pc >= length + || end_pc < 0 || end_pc > length || start_pc >= end_pc + || handler_pc < 0 || handler_pc >= length + || (handler_pc >= start_pc && handler_pc < end_pc) + || ! (instruction_bits [start_pc] & BCODE_INSTRUCTION_START) + || ! (instruction_bits [end_pc] & BCODE_INSTRUCTION_START) + || ! (instruction_bits [handler_pc] & BCODE_INSTRUCTION_START)) + { + error ("bad pc in exception_table"); + return 0; + } + + if (! add_handler (start_pc, end_pc, + lookup_label (handler_pc), + catch_type == 0 ? NULL_TREE + : get_class_constant (jcf, catch_type))) + { + error ("overlapping exception ranges are not supported"); + return 0; + } + + instruction_bits [handler_pc] |= BCODE_EXCEPTION_TARGET; + } + + for (PC = 0;;) + { + int index; + tree type, tmp; + if (((PC != INVALID_PC + && instruction_bits [PC] & BCODE_TARGET) != 0) + || PC == 0) + { + PUSH_PENDING (lookup_label (PC)); + INVALIDATE_PC; + } + if (PC == INVALID_PC) + { + label = pending_blocks; + if (label == NULL_TREE) + break; /* We're done! */ + pending_blocks = LABEL_PENDING_CHAIN (label); + LABEL_CHANGED (label) = 0; + + if (LABEL_IN_SUBR (label)) + current_subr = LABEL_SUBR_START (label); + else + current_subr = NULL_TREE; + + /* Restore type_map and stack_pointer from + LABEL_TYPE_STATE (label), and continue + compiling from there. */ + load_type_state (label); + PC = LABEL_PC (label); + } + else if (PC >= length) + VERIFICATION_ERROR ("falling through end of method"); + + oldpc = PC; + + if (!(instruction_bits [PC] & BCODE_INSTRUCTION_START) && ! wide) + VERIFICATION_ERROR ("PC not at instruction start"); + + instruction_bits[PC] |= BCODE_VERIFIED; + + eh_ranges = find_handler (oldpc); + + op_code = byte_ops[PC++]; + switch (op_code) + { + int is_static, is_putting; + case OPCODE_nop: + break; + case OPCODE_iconst_m1: + case OPCODE_iconst_0: case OPCODE_iconst_1: case OPCODE_iconst_2: + case OPCODE_iconst_3: case OPCODE_iconst_4: case OPCODE_iconst_5: + i = op_code - OPCODE_iconst_0; + goto push_int; + push_int: + if (byte_ops[PC] == OPCODE_newarray + || byte_ops[PC] == OPCODE_newarray) + int_value = i; + push_type (int_type_node); break; + case OPCODE_lconst_0: case OPCODE_lconst_1: + push_type (long_type_node); break; + case OPCODE_fconst_0: case OPCODE_fconst_1: case OPCODE_fconst_2: + push_type (float_type_node); break; + case OPCODE_dconst_0: case OPCODE_dconst_1: + push_type (double_type_node); break; + case OPCODE_bipush: + i = IMMEDIATE_s1; + goto push_int; + case OPCODE_sipush: + i = IMMEDIATE_s2; + goto push_int; + case OPCODE_iload: type = int_type_node; goto general_load; + case OPCODE_lload: type = long_type_node; goto general_load; + case OPCODE_fload: type = float_type_node; goto general_load; + case OPCODE_dload: type = double_type_node; goto general_load; + case OPCODE_aload: type = ptr_type_node; goto general_load; + general_load: + index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1; + wide = 0; + goto load; + case OPCODE_iload_0: type = int_type_node; index = 0; goto load; + case OPCODE_iload_1: type = int_type_node; index = 1; goto load; + case OPCODE_iload_2: type = int_type_node; index = 2; goto load; + case OPCODE_iload_3: type = int_type_node; index = 3; goto load; + case OPCODE_lload_0: type = long_type_node; index = 0; goto load; + case OPCODE_lload_1: type = long_type_node; index = 1; goto load; + case OPCODE_lload_2: type = long_type_node; index = 2; goto load; + case OPCODE_lload_3: type = long_type_node; index = 3; goto load; + case OPCODE_fload_0: type = float_type_node; index = 0; goto load; + case OPCODE_fload_1: type = float_type_node; index = 1; goto load; + case OPCODE_fload_2: type = float_type_node; index = 2; goto load; + case OPCODE_fload_3: type = float_type_node; index = 3; goto load; + case OPCODE_dload_0: type = double_type_node; index = 0; goto load; + case OPCODE_dload_1: type = double_type_node; index = 1; goto load; + case OPCODE_dload_2: type = double_type_node; index = 2; goto load; + case OPCODE_dload_3: type = double_type_node; index = 3; goto load; + case OPCODE_aload_0: type = ptr_type_node; index = 0; goto load; + case OPCODE_aload_1: type = ptr_type_node; index = 1; goto load; + case OPCODE_aload_2: type = ptr_type_node; index = 2; goto load; + case OPCODE_aload_3: type = ptr_type_node; index = 3; goto load; + load: + if (index < 0 + || (index + TYPE_IS_WIDE (type) + >= DECL_MAX_LOCALS (current_function_decl))) + VERIFICATION_ERROR ("invalid local variable index in load"); + tmp = type_map[index]; + if (tmp == TYPE_UNKNOWN || tmp == TYPE_SECOND + || (TYPE_IS_WIDE (type) + && type_map[index+1] != void_type_node) + || (type == ptr_type_node + ? TREE_CODE (tmp) != POINTER_TYPE + : type == int_type_node + ? (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32) + : type != tmp)) + VERIFICATION_ERROR("invalid local variable type in load"); + push_type (tmp); + goto note_used; + case OPCODE_istore: type = int_type_node; goto general_store; + case OPCODE_lstore: type = long_type_node; goto general_store; + case OPCODE_fstore: type = float_type_node; goto general_store; + case OPCODE_dstore: type = double_type_node; goto general_store; + case OPCODE_astore: type = ptr_type_node; goto general_store; + general_store: + index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1; + wide = 0; + goto store; + case OPCODE_istore_0: type = int_type_node; index = 0; goto store; + case OPCODE_istore_1: type = int_type_node; index = 1; goto store; + case OPCODE_istore_2: type = int_type_node; index = 2; goto store; + case OPCODE_istore_3: type = int_type_node; index = 3; goto store; + case OPCODE_lstore_0: type = long_type_node; index=0; goto store; + case OPCODE_lstore_1: type = long_type_node; index=1; goto store; + case OPCODE_lstore_2: type = long_type_node; index=2; goto store; + case OPCODE_lstore_3: type = long_type_node; index=3; goto store; + case OPCODE_fstore_0: type=float_type_node; index=0; goto store; + case OPCODE_fstore_1: type=float_type_node; index=1; goto store; + case OPCODE_fstore_2: type=float_type_node; index=2; goto store; + case OPCODE_fstore_3: type=float_type_node; index=3; goto store; + case OPCODE_dstore_0: type=double_type_node; index=0; goto store; + case OPCODE_dstore_1: type=double_type_node; index=1; goto store; + case OPCODE_dstore_2: type=double_type_node; index=2; goto store; + case OPCODE_dstore_3: type=double_type_node; index=3; goto store; + case OPCODE_astore_0: type = ptr_type_node; index = 0; goto store; + case OPCODE_astore_1: type = ptr_type_node; index = 1; goto store; + case OPCODE_astore_2: type = ptr_type_node; index = 2; goto store; + case OPCODE_astore_3: type = ptr_type_node; index = 3; goto store; + store: + if (index < 0 + || (index + TYPE_IS_WIDE (type) + >= DECL_MAX_LOCALS (current_function_decl))) + { + VERIFICATION_ERROR ("invalid local variable index in store"); + return 0; + } + type = pop_type (type); + type_map[index] = type; + + /* If local variable changed, we need to reconsider eh handlers. */ + prev_eh_ranges = NULL_EH_RANGE; + + /* Allocate decl and rtx for this variable now, so if we're not + optmizing, we get a temporary that survives the whole method. */ + find_local_variable (index, type, oldpc); + + if (TYPE_IS_WIDE (type)) + type_map[index+1] = TYPE_SECOND; + /* ... fall through to note_used ... */ + note_used: + /* For store or load, note that local variable INDEX is used. + This is needed to verify try-finally sub-routines. */ + if (current_subr) + { + tree vec = LABEL_RETURN_TYPE_STATE (current_subr); + tree subr_vec = LABEL_TYPE_STATE (current_subr); + int len = 1 + TYPE_IS_WIDE (type); + while (--len >= 0) + { + if (TREE_VEC_ELT (vec, index) == TYPE_UNUSED) + TREE_VEC_ELT (vec, index) = TREE_VEC_ELT (subr_vec, index); + } + } + break; + case OPCODE_iadd: + case OPCODE_iand: + case OPCODE_idiv: + case OPCODE_imul: + case OPCODE_ior: + case OPCODE_irem: + case OPCODE_ishl: + case OPCODE_ishr: + case OPCODE_isub: + case OPCODE_iushr: + case OPCODE_ixor: + type = int_type_node; goto binop; + case OPCODE_ineg: + case OPCODE_i2c: + case OPCODE_i2b: + case OPCODE_i2s: + type = int_type_node; goto unop; + case OPCODE_ladd: + case OPCODE_land: + case OPCODE_ldiv: + case OPCODE_lsub: + case OPCODE_lmul: + case OPCODE_lrem: + case OPCODE_lor: + case OPCODE_lxor: + type = long_type_node; goto binop; + case OPCODE_lneg: + type = long_type_node; goto unop; + case OPCODE_fadd: case OPCODE_fsub: + case OPCODE_fmul: case OPCODE_fdiv: case OPCODE_frem: + type = float_type_node; goto binop; + case OPCODE_fneg: + type = float_type_node; goto unop; + case OPCODE_dadd: case OPCODE_dsub: + case OPCODE_dmul: case OPCODE_ddiv: case OPCODE_drem: + type = double_type_node; goto binop; + case OPCODE_dneg: + type = double_type_node; goto unop; + unop: + pop_type (type); + push_type (type); + break; + binop: + pop_type (type); + pop_type (type); + push_type (type); + break; + case OPCODE_lshl: + case OPCODE_lshr: + case OPCODE_lushr: + pop_type (int_type_node); + pop_type (long_type_node); + push_type (long_type_node); + break; + case OPCODE_iinc: + index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1; + PC += wide + 1; + wide = 0; + if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl)) + VERIFICATION_ERROR ("invalid local variable index in iinc"); + tmp = type_map[index]; + if (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32) + VERIFICATION_ERROR ("invalid local variable type in iinc"); + break; + case OPCODE_i2l: + pop_type (int_type_node); push_type (long_type_node); break; + case OPCODE_i2f: + pop_type (int_type_node); push_type (float_type_node); break; + case OPCODE_i2d: + pop_type (int_type_node); push_type (double_type_node); break; + case OPCODE_l2i: + pop_type (long_type_node); push_type (int_type_node); break; + case OPCODE_l2f: + pop_type (long_type_node); push_type (float_type_node); break; + case OPCODE_l2d: + pop_type (long_type_node); push_type (double_type_node); break; + case OPCODE_f2i: + pop_type (float_type_node); push_type (int_type_node); break; + case OPCODE_f2l: + pop_type (float_type_node); push_type (long_type_node); break; + case OPCODE_f2d: + pop_type (float_type_node); push_type (double_type_node); break; + case OPCODE_d2i: + pop_type (double_type_node); push_type (int_type_node); break; + case OPCODE_d2l: + pop_type (double_type_node); push_type (long_type_node); break; + case OPCODE_d2f: + pop_type (double_type_node); push_type (float_type_node); break; + case OPCODE_lcmp: + type = long_type_node; goto compare; + case OPCODE_fcmpl: + case OPCODE_fcmpg: + type = float_type_node; goto compare; + case OPCODE_dcmpl: + case OPCODE_dcmpg: + type = double_type_node; goto compare; + compare: + pop_type (type); pop_type (type); + push_type (int_type_node); break; + case OPCODE_ifeq: + case OPCODE_ifne: + case OPCODE_iflt: + case OPCODE_ifge: + case OPCODE_ifgt: + case OPCODE_ifle: + pop_type (int_type_node); goto cond; + case OPCODE_ifnull: + case OPCODE_ifnonnull: + pop_type (ptr_type_node ); goto cond; + case OPCODE_if_icmpeq: + case OPCODE_if_icmpne: + case OPCODE_if_icmplt: + case OPCODE_if_icmpge: + case OPCODE_if_icmpgt: + case OPCODE_if_icmple: + pop_type (int_type_node); pop_type (int_type_node); goto cond; + case OPCODE_if_acmpeq: + case OPCODE_if_acmpne: + pop_type (object_ptr_type_node); pop_type (object_ptr_type_node); + goto cond; + cond: + PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2)); + break; + case OPCODE_goto: + PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2)); + INVALIDATE_PC; + break; + case OPCODE_wide: + switch (byte_ops[PC]) + { + case OPCODE_iload: case OPCODE_lload: + case OPCODE_fload: case OPCODE_dload: case OPCODE_aload: + case OPCODE_istore: case OPCODE_lstore: + case OPCODE_fstore: case OPCODE_dstore: case OPCODE_astore: + case OPCODE_iinc: + case OPCODE_ret: + wide = 1; + break; + default: + VERIFICATION_ERROR ("invalid use of wide instruction"); + } + break; + case OPCODE_ireturn: type = int_type_node; goto ret; + case OPCODE_lreturn: type = long_type_node; goto ret; + case OPCODE_freturn: type = float_type_node; goto ret; + case OPCODE_dreturn: type = double_type_node; goto ret; + case OPCODE_areturn: type = ptr_type_node; goto ret; + ret: + pop_type (type); + /* ... fall through ... */ + case OPCODE_return: + INVALIDATE_PC; + break; + case OPCODE_getstatic: is_putting = 0; is_static = 1; goto field; + case OPCODE_putstatic: is_putting = 1; is_static = 1; goto field; + case OPCODE_getfield: is_putting = 0; is_static = 0; goto field; + case OPCODE_putfield: is_putting = 1; is_static = 0; goto field; + field: + { + int index = IMMEDIATE_u2; + tree self_type = get_class_constant + (jcf, COMPONENT_REF_CLASS_INDEX (¤t_jcf->cpool, index)); + tree field_name = COMPONENT_REF_NAME (¤t_jcf->cpool, index); + tree field_signature = COMPONENT_REF_SIGNATURE (¤t_jcf->cpool, index); + tree field_type = get_type_from_signature (field_signature); + if (is_putting) + pop_type (field_type); + if (! is_static) + { + /* Defer actual checking until next pass. */ + pop_type (ptr_type_node); + } + if (! is_putting) + push_type (field_type); + break; + } + case OPCODE_new: + push_type (get_class_constant (jcf, IMMEDIATE_u2)); + break; + case OPCODE_dup: type_stack_dup (1, 0); break; + case OPCODE_dup_x1: type_stack_dup (1, 1); break; + case OPCODE_dup_x2: type_stack_dup (1, 2); break; + case OPCODE_dup2: type_stack_dup (2, 0); break; + case OPCODE_dup2_x1: type_stack_dup (2, 1); break; + case OPCODE_dup2_x2: type_stack_dup (2, 2); break; + case OPCODE_pop: index = 1; goto pop; + case OPCODE_pop2: index = 2; goto pop; + pop: + if (stack_pointer < index) + VERIFICATION_ERROR ("stack underflow"); + stack_pointer -= index; + break; + case OPCODE_swap: + if (stack_pointer < 2) + VERIFICATION_ERROR ("stack underflow (in swap)"); + else + { + tree type1 = stack_type_map[stack_pointer - 1]; + tree type2 = stack_type_map[stack_pointer - 2]; + if (type1 == void_type_node || type2 == void_type_node) + VERIFICATION_ERROR ("verifier (swap): double or long value"); + stack_type_map[stack_pointer - 2] = type1; + stack_type_map[stack_pointer - 1] = type2; + } + break; + case OPCODE_ldc: index = IMMEDIATE_u1; goto ldc; + case OPCODE_ldc2_w: + case OPCODE_ldc_w: + index = IMMEDIATE_u2; goto ldc; + ldc: + if (index <= 0 || index >= JPOOL_SIZE(current_jcf)) + VERIFICATION_ERROR ("bad constant pool index in ldc"); + int_value = -1; + switch (JPOOL_TAG (current_jcf, index) & ~CONSTANT_ResolvedFlag) + { + case CONSTANT_Integer: type = int_type_node; goto check_ldc; + case CONSTANT_Float: type = float_type_node; goto check_ldc; + case CONSTANT_String: type = string_type_node; goto check_ldc; + case CONSTANT_Long: type = long_type_node; goto check_ldc; + case CONSTANT_Double: type = double_type_node; goto check_ldc; + check_ldc: + if (TYPE_IS_WIDE (type) == (op_code == OPCODE_ldc2_w)) + break; + /* ... else fall through ... */ + default: + bad_ldc: + VERIFICATION_ERROR ("bad constant pool tag in ldc"); + } + if (type == int_type_node) + { + i = TREE_INT_CST_LOW (get_constant (current_jcf, index)); + goto push_int; + } + push_type (type); + break; + + case OPCODE_invokevirtual: + case OPCODE_invokespecial: + case OPCODE_invokestatic: + case OPCODE_invokeinterface: + { + int index = IMMEDIATE_u2; + tree sig = COMPONENT_REF_SIGNATURE (¤t_jcf->cpool, index); + tree self_type = get_class_constant + (current_jcf, COMPONENT_REF_CLASS_INDEX (¤t_jcf->cpool, + index)); + tree method_name = COMPONENT_REF_NAME (¤t_jcf->cpool, index); + tree method_type; + method_type = parse_signature_string (IDENTIFIER_POINTER (sig), + IDENTIFIER_LENGTH (sig)); + if (TREE_CODE (method_type) != FUNCTION_TYPE) + VERIFICATION_ERROR ("bad method signature"); + pop_argument_types (TYPE_ARG_TYPES (method_type)); + + /* Can't invoke <clinit> */ + if (method_name == clinit_identifier_node) + VERIFICATION_ERROR ("invoke opcode can't invoke <clinit>"); + /* Apart invokespecial, can't invoke <init> */ + if (op_code != OPCODE_invokespecial + && method_name == init_identifier_node) + VERIFICATION_ERROR ("invoke opcode can't invoke <init>"); + + if (op_code != OPCODE_invokestatic) + pop_type (self_type); + + switch (op_code) + { + case OPCODE_invokeinterface: + { + int nargs = IMMEDIATE_u1; + int notZero = IMMEDIATE_u1; + + if (!nargs || notZero) + VERIFICATION_ERROR + ("invalid argument number in invokeinterface"); + break; + } + } + + if (TREE_TYPE (method_type) != void_type_node) + push_type (TREE_TYPE (method_type)); + break; + } + + case OPCODE_arraylength: + /* Type checking actually made during code generation */ + pop_type( ptr_type_node ); + push_type( int_type_node ); + break; + + /* Q&D verification *or* more checking done during code generation + for byte/boolean/char/short, the value popped is a int coerced + into the right type before being stored. */ + case OPCODE_iastore: type = int_type_node; goto astore; + case OPCODE_lastore: type = long_type_node; goto astore; + case OPCODE_fastore: type = float_type_node; goto astore; + case OPCODE_dastore: type = double_type_node; goto astore; + case OPCODE_aastore: type = ptr_type_node; goto astore; + case OPCODE_bastore: type = int_type_node; goto astore; + case OPCODE_castore: type = int_type_node; goto astore; + case OPCODE_sastore: type = int_type_node; goto astore; + astore: + /* FIXME - need better verification here */ + pop_type (type); /* new value */ + pop_type (int_type_node); /* index */ + pop_type (ptr_type_node); /* array */ + break; + + /* Q&D verification *or* more checking done during code generation + for byte/boolean/char/short, the value pushed is a int. */ + case OPCODE_iaload: type = int_type_node; goto aload; + case OPCODE_laload: type = long_type_node; goto aload; + case OPCODE_faload: type = float_type_node; goto aload; + case OPCODE_daload: type = double_type_node; goto aload; + case OPCODE_aaload: type = ptr_type_node; goto aload; + case OPCODE_baload: type = promote_type (byte_type_node); goto aload; + case OPCODE_caload: type = promote_type (char_type_node); goto aload; + case OPCODE_saload: type = promote_type (short_type_node); goto aload; + aload: + pop_type (int_type_node); + type = pop_type (ptr_type_node); + if (! is_array_type_p (type)) + VERIFICATION_ERROR ("array load from non-array type"); + push_type (TYPE_ARRAY_ELEMENT (TREE_TYPE (type))); + break; + + case OPCODE_anewarray: + type = get_class_constant (current_jcf, IMMEDIATE_u2); + type = promote_type (type); + goto newarray; + + case OPCODE_newarray: + index = IMMEDIATE_u1; + type = decode_newarray_type (index); + if (type == NULL_TREE) + VERIFICATION_ERROR ("invalid type code in newarray opcode"); + goto newarray; + + newarray: + if (int_value >= 0 && prevpc >= 0) + { + /* If previous instruction pushed int constant, + we want to use it. */ + switch (byte_ops[prevpc]) + { + case OPCODE_iconst_0: case OPCODE_iconst_1: + case OPCODE_iconst_2: case OPCODE_iconst_3: + case OPCODE_iconst_4: case OPCODE_iconst_5: + case OPCODE_bipush: case OPCODE_sipush: + case OPCODE_ldc: case OPCODE_ldc_w: + break; + default: + int_value = -1; + } + } + else + int_value = -1; + type = build_java_array_type (type, int_value); + pop_type (int_type_node); + push_type (type); + break; + + case OPCODE_multianewarray: + { + int ndim, i; + index = IMMEDIATE_u2; + ndim = IMMEDIATE_u1; + + if( ndim < 1 ) + VERIFICATION_ERROR ("number of dimension lower that 1 in multianewarray" ); + + for( i = 0; i < ndim; i++ ) + pop_type (int_type_node); + push_type (get_class_constant (current_jcf, index)); + break; + } + + case OPCODE_aconst_null: + push_type (ptr_type_node); + break; + + case OPCODE_athrow: + pop_type (throwable_type_node); + INVALIDATE_PC; + break; + + case OPCODE_checkcast: + pop_type (ptr_type_node); + type = get_class_constant (current_jcf, IMMEDIATE_u2); + push_type (type); + break; + case OPCODE_instanceof: + pop_type (ptr_type_node); + get_class_constant (current_jcf, IMMEDIATE_u2); + push_type (integer_type_node); + break; + + case OPCODE_tableswitch: + { + jint default_val, low, high; + + pop_type (integer_type_node); + while (PC%4) + { + if (byte_ops[PC++]) + VERIFICATION_ERROR ("bad alignment in tableswitch pad"); + } + PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4)); + low = IMMEDIATE_s4; + high = IMMEDIATE_s4; + + if (low > high) + VERIFICATION_ERROR ("unsorted low/high value in tableswitch"); + + while (low++ <= high) + PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4)); + break; + } + + case OPCODE_lookupswitch: + { + jint npairs, last, not_registered = 1; + + pop_type (integer_type_node); + while (PC%4) + { + if (byte_ops[PC++]) + VERIFICATION_ERROR ("bad alignment in lookupswitch pad"); + } + + PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4)); + npairs = IMMEDIATE_s4; + + if (npairs < 0) + VERIFICATION_ERROR ("invalid number of targets in lookupswitch"); + + while (npairs--) + { + int match = IMMEDIATE_s4; + if (not_registered) + not_registered = 0; + else if (last >= match) + VERIFICATION_ERROR ("unsorted match value in lookupswitch"); + + last = match; + PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4)); + } + break; + } + + case OPCODE_monitorenter: + /* fall thru */ + case OPCODE_monitorexit: + pop_type (ptr_type_node); + break; + + case OPCODE_goto_w: + PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4)); + INVALIDATE_PC; + break; + + case OPCODE_jsr: + { + tree target = lookup_label (oldpc + IMMEDIATE_s2); + tree return_label = lookup_label (PC); + push_type (return_address_type_node); + if (! LABEL_VERIFIED (target)) + { + /* first time seen */ + tree return_type_map; + int nlocals = DECL_MAX_LOCALS (current_function_decl); + index = nlocals + DECL_MAX_STACK (current_function_decl); + return_type_map = make_tree_vec (index); + while (--index >= nlocals) + TREE_VEC_ELT (return_type_map, index) = TYPE_UNKNOWN; + while (--index >= 0) + TREE_VEC_ELT (return_type_map, index) = TYPE_UNUSED; + LABEL_RETURN_LABEL (target) + = build_decl (LABEL_DECL, NULL_TREE, TREE_TYPE (target)); + LABEL_PC (LABEL_RETURN_LABEL (target)) = -1; + LABEL_RETURN_TYPE_STATE (target) = return_type_map; + LABEL_IS_SUBR_START (target) = 1; + LABEL_IN_SUBR (target) = 1; + LABEL_SUBR_START (target) = target; + LABEL_SUBR_CONTEXT (target) = current_subr; + } + else if (! LABEL_IS_SUBR_START (target) + || LABEL_SUBR_CONTEXT (target) != current_subr) + VERIFICATION_ERROR ("label part of different subroutines"); + + i = merge_type_state (target); + if (i != 0) + { + if (i < 0) + VERIFICATION_ERROR ("types could not be merged at jsr"); + push_pending_label (target); + } + current_subr = target; + + /* Chain return_pc onto LABEL_RETURN_LABELS (target) if needed. */ + if (! value_member (return_label, LABEL_RETURN_LABELS (target))) + { + LABEL_RETURN_LABELS (target) + = tree_cons (NULL_TREE, return_label, + LABEL_RETURN_LABELS (target)); + } + + if (LABEL_VERIFIED (target)) + { + tree return_map = LABEL_RETURN_TYPE_STATE (target); + int len = TREE_VEC_LENGTH (return_map); + stack_pointer = len - DECL_MAX_LOCALS (current_function_decl); + while (--len >= 0) + { + if (TREE_VEC_ELT (return_map, len) != TYPE_UNUSED) + type_map[len] = TREE_VEC_ELT (return_map, len); + } + current_subr = LABEL_SUBR_CONTEXT (target); + PUSH_PENDING (return_label); + } + + INVALIDATE_PC; + } + break; + case OPCODE_ret: + if (current_subr == NULL) + VERIFICATION_ERROR ("ret instruction not in a jsr subroutine"); + else + { + tree ret_map = LABEL_RETURN_TYPE_STATE (current_subr); + tree caller = LABEL_SUBR_CONTEXT (current_subr); + int size = DECL_MAX_LOCALS(current_function_decl)+stack_pointer; + index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1; + wide = 0; + INVALIDATE_PC; + if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl) + || type_map[index] != TYPE_RETURN_ADDR) + VERIFICATION_ERROR ("invalid ret index"); + + /* The next chunk of code is similar to an inlined version of + * merge_type_state (LABEL_RETURN_LABEL (current_subr)). + * The main differences are that LABEL_RETURN_LABEL is + * pre-allocated by the jsr (but we don't know the size then); + * and that we have to handle TYPE_UNUSED. */ + + if (! RETURN_MAP_ADJUSTED (ret_map)) + { /* First return from this subroutine - fix stack pointer. */ + TREE_VEC_LENGTH (ret_map) = size; + for (index = size; --index >= 0; ) + { + if (TREE_VEC_ELT (ret_map, index) != TYPE_UNUSED) + TREE_VEC_ELT (ret_map, index) = type_map[index]; + } + RETURN_MAP_ADJUSTED (ret_map) = 1; + } + else + { + if (TREE_VEC_LENGTH (ret_map) != size) + VERIFICATION_ERROR ("inconsistent stack size on ret"); + for (index = 0; index < size; index++) + { + tree type = TREE_VEC_ELT (ret_map, index); + if (type != TYPE_UNUSED) + { + type = merge_types (type, type_map [index]); + TREE_VEC_ELT (ret_map, index) = type; + if (type == TYPE_UNKNOWN) + { + if (index >= size - stack_pointer) + VERIFICATION_ERROR + ("inconsistent types on ret from jsr"); + } + else if (TYPE_IS_WIDE (type)) + index++; + } + } + } + + /* Check if there are any more pending blocks in this subroutine. + Because we push pending blocks in a last-in-first-out order, + and because we don't push anything from our caller until we + are done with this subroutine or anything nested in it, + then we are done if the top of the pending_blocks stack is + not in a subroutine, or it is in our caller. */ + if (pending_blocks == NULL_TREE + || ! LABEL_IN_SUBR (pending_blocks) + || LABEL_SUBR_START (pending_blocks) == caller) + { + /* Since we are done with this subroutine (i.e. this is the + last ret from it), set up the (so far known) return + address as pending - with the merged type state. */ + tmp = LABEL_RETURN_LABELS (current_subr); + current_subr = caller; + for ( ; tmp != NULL_TREE; tmp = TREE_CHAIN (tmp)) + { + tree return_label = TREE_VALUE (tmp); + tree return_state = LABEL_TYPE_STATE (return_label); + if (return_state == NULL_TREE) + { + /* This means means we had not verified the + subroutine earlier, so this is the first jsr to + call it. In this case, the type_map of the return + address is just the current type_map - and that + is handled by the following PUSH_PENDING. */ + } + else + { + /* In this case we have to do a merge. But first + restore the type_map for unused slots to those + that were in effect at the jsr. */ + for (index = size; --index >= 0; ) + { + type_map[index] = TREE_VEC_ELT (ret_map, index); + if (type_map[index] == TYPE_UNUSED) + type_map[index] + = TREE_VEC_ELT (return_state, index); + } + } + PUSH_PENDING (return_label); + } + } + } + break; + case OPCODE_jsr_w: + case OPCODE_ret_w: + default: + error ("unknown opcode %d@pc=%d during verification", op_code, PC-1); + return 0; + } + + prevpc = oldpc; + + /* The following test is true if we have entered or exited an exception + handler range *or* we have done a store to a local variable. + In either case we need to consider any exception handlers that + might "follow" this instruction. */ + + if (eh_ranges != prev_eh_ranges) + { + int save_stack_pointer = stack_pointer; + int index = DECL_MAX_LOCALS (current_function_decl); + tree save_type = type_map[index]; + tree save_current_subr = current_subr; + struct eh_range *ranges = find_handler (oldpc); + stack_pointer = 1; + for (; ranges != NULL_EH_RANGE; ranges = ranges->outer) + { + tree chain = ranges->handlers; + + /* We need to determine if the handler is part of current_subr. + The are two cases: (1) The exception catch range + is entirely within current_subr. In that case the handler + is also part of current_subr. + (2) Some of the catch range is not in current_subr. + In that case, the handler is *not* part of current_subr. + + Figuring out which is the case is not necessarily obvious, + in the presence of clever code generators (and obfuscators). + We make a simplifying assumption that in case (2) we + have that the current_subr is entirely within the catch range. + In that case we can assume if that if a caller (the jsr) of + a subroutine is within the catch range, then the handler is + *not* part of the subroutine, and vice versa. */ + + current_subr = save_current_subr; + for ( ; current_subr != NULL_TREE; + current_subr = LABEL_SUBR_CONTEXT (current_subr)) + { + tree return_labels = LABEL_RETURN_LABELS (current_subr); + /* There could be multiple return_labels, but + we only need to check one. */ + int return_pc = LABEL_PC (TREE_VALUE (return_labels)); + if (return_pc <= ranges->start_pc + || return_pc > ranges->end_pc) + break; + } + + for ( ; chain != NULL_TREE; chain = TREE_CHAIN (chain)) + { + tree handler = TREE_VALUE (chain); + tree type = TREE_PURPOSE (chain); + if (type == NULL_TREE) /* a finally handler */ + type = throwable_type_node; + type_map[index] = promote_type (type); + + PUSH_PENDING (handler); + } + } + stack_pointer = save_stack_pointer; + current_subr = save_current_subr; + type_map[index] = save_type; + prev_eh_ranges = eh_ranges; + } + } + return 1; + bad_pc: + message = "program counter out of range"; + goto verify_error; + verify_error: + error ("verification error at PC=%d: %s", oldpc, message); + return 0; +} |