/* Copyright (C) 2021-2024 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program 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 3, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include #include #include #include "util.h" #include "DbeSession.h" #include "ClassFile.h" #include "Function.h" #include "StringBuilder.h" #include "DbeFile.h" class ByteCodeInfo { public: ByteCodeInfo (JMethod *_func, int _bci, int _lno) { func = _func; bci = _bci; lno = _lno; }; JMethod *func; int bci; int lno; }; typedef unsigned char u1; typedef unsigned short u2; typedef unsigned int u4; // Class File Constants #define JAVA_MAGIC 0xcafebabe enum { // First argument in access_flags_to_str() ClassAccess = 1, FieldAccess, MethodAccess, NestedClassAccess, // jdk/src/share/classes/sun/tools/java/RuntimeConstants.java // Type codes T_CLASS = 0x00000002, T_BOOLEAN = 0x00000004, T_CHAR = 0x00000005, T_FLOAT = 0x00000006, T_DOUBLE = 0x00000007, T_BYTE = 0x00000008, T_SHORT = 0x00000009, T_INT = 0x0000000a, T_LONG = 0x0000000b, // Access and modifier flags ACC_PUBLIC = 0x00000001, ACC_PRIVATE = 0x00000002, ACC_PROTECTED = 0x00000004, ACC_STATIC = 0x00000008, ACC_FINAL = 0x00000010, ACC_SYNCHRONIZED = 0x00000020, ACC_VOLATILE = 0x00000040, ACC_TRANSIENT = 0x00000080, ACC_NATIVE = 0x00000100, ACC_INTERFACE = 0x00000200, ACC_ABSTRACT = 0x00000400, ACC_STRICT = 0x00000800, ACC_SYNTHETIC = 0x00001000, ACC_ANNOTATION = 0x00002000, ACC_ENUM = 0x00004000, ACC_SUPER = 0x00000020, ACC_BRIDGE = 0x00000040, ACC_VARARGS = 0x00000080, // Opcodes opc_try = -3, opc_dead = -2, opc_label = -1, opc_nop = 0, opc_aconst_null = 1, opc_iconst_m1 = 2, opc_iconst_0 = 3, opc_iconst_1 = 4, opc_iconst_2 = 5, opc_iconst_3 = 6, opc_iconst_4 = 7, opc_iconst_5 = 8, opc_lconst_0 = 9, opc_lconst_1 = 10, opc_fconst_0 = 11, opc_fconst_1 = 12, opc_fconst_2 = 13, opc_dconst_0 = 14, opc_dconst_1 = 15, opc_bipush = 16, opc_sipush = 17, opc_ldc = 18, opc_ldc_w = 19, opc_ldc2_w = 20, opc_iload = 21, opc_lload = 22, opc_fload = 23, opc_dload = 24, opc_aload = 25, opc_iload_0 = 26, opc_iload_1 = 27, opc_iload_2 = 28, opc_iload_3 = 29, opc_lload_0 = 30, opc_lload_1 = 31, opc_lload_2 = 32, opc_lload_3 = 33, opc_fload_0 = 34, opc_fload_1 = 35, opc_fload_2 = 36, opc_fload_3 = 37, opc_dload_0 = 38, opc_dload_1 = 39, opc_dload_2 = 40, opc_dload_3 = 41, opc_aload_0 = 42, opc_aload_1 = 43, opc_aload_2 = 44, opc_aload_3 = 45, opc_iaload = 46, opc_laload = 47, opc_faload = 48, opc_daload = 49, opc_aaload = 50, opc_baload = 51, opc_caload = 52, opc_saload = 53, opc_istore = 54, opc_lstore = 55, opc_fstore = 56, opc_dstore = 57, opc_astore = 58, opc_istore_0 = 59, opc_istore_1 = 60, opc_istore_2 = 61, opc_istore_3 = 62, opc_lstore_0 = 63, opc_lstore_1 = 64, opc_lstore_2 = 65, opc_lstore_3 = 66, opc_fstore_0 = 67, opc_fstore_1 = 68, opc_fstore_2 = 69, opc_fstore_3 = 70, opc_dstore_0 = 71, opc_dstore_1 = 72, opc_dstore_2 = 73, opc_dstore_3 = 74, opc_astore_0 = 75, opc_astore_1 = 76, opc_astore_2 = 77, opc_astore_3 = 78, opc_iastore = 79, opc_lastore = 80, opc_fastore = 81, opc_dastore = 82, opc_aastore = 83, opc_bastore = 84, opc_castore = 85, opc_sastore = 86, opc_pop = 87, opc_pop2 = 88, opc_dup = 89, opc_dup_x1 = 90, opc_dup_x2 = 91, opc_dup2 = 92, opc_dup2_x1 = 93, opc_dup2_x2 = 94, opc_swap = 95, opc_iadd = 96, opc_ladd = 97, opc_fadd = 98, opc_dadd = 99, opc_isub = 100, opc_lsub = 101, opc_fsub = 102, opc_dsub = 103, opc_imul = 104, opc_lmul = 105, opc_fmul = 106, opc_dmul = 107, opc_idiv = 108, opc_ldiv = 109, opc_fdiv = 110, opc_ddiv = 111, opc_irem = 112, opc_lrem = 113, opc_frem = 114, opc_drem = 115, opc_ineg = 116, opc_lneg = 117, opc_fneg = 118, opc_dneg = 119, opc_ishl = 120, opc_lshl = 121, opc_ishr = 122, opc_lshr = 123, opc_iushr = 124, opc_lushr = 125, opc_iand = 126, opc_land = 127, opc_ior = 128, opc_lor = 129, opc_ixor = 130, opc_lxor = 131, opc_iinc = 132, opc_i2l = 133, opc_i2f = 134, opc_i2d = 135, opc_l2i = 136, opc_l2f = 137, opc_l2d = 138, opc_f2i = 139, opc_f2l = 140, opc_f2d = 141, opc_d2i = 142, opc_d2l = 143, opc_d2f = 144, opc_i2b = 145, opc_i2c = 146, opc_i2s = 147, opc_lcmp = 148, opc_fcmpl = 149, opc_fcmpg = 150, opc_dcmpl = 151, opc_dcmpg = 152, opc_ifeq = 153, opc_ifne = 154, opc_iflt = 155, opc_ifge = 156, opc_ifgt = 157, opc_ifle = 158, opc_if_icmpeq = 159, opc_if_icmpne = 160, opc_if_icmplt = 161, opc_if_icmpge = 162, opc_if_icmpgt = 163, opc_if_icmple = 164, opc_if_acmpeq = 165, opc_if_acmpne = 166, opc_goto = 167, opc_jsr = 168, opc_ret = 169, opc_tableswitch = 170, opc_lookupswitch = 171, opc_ireturn = 172, opc_lreturn = 173, opc_freturn = 174, opc_dreturn = 175, opc_areturn = 176, opc_return = 177, opc_getstatic = 178, opc_putstatic = 179, opc_getfield = 180, opc_putfield = 181, opc_invokevirtual = 182, opc_invokespecial = 183, opc_invokestatic = 184, opc_invokeinterface = 185, opc_invokedynamic = 186, opc_new = 187, opc_newarray = 188, opc_anewarray = 189, opc_arraylength = 190, opc_athrow = 191, opc_checkcast = 192, opc_instanceof = 193, opc_monitorenter = 194, opc_monitorexit = 195, opc_wide = 196, opc_multianewarray = 197, opc_ifnull = 198, opc_ifnonnull = 199, opc_goto_w = 200, opc_jsr_w = 201, opc_breakpoint = 202, // Constant table CONSTANT_UTF8 = 1, CONSTANT_UNICODE = 2, CONSTANT_INTEGER = 3, CONSTANT_FLOAT = 4, CONSTANT_LONG = 5, CONSTANT_DOUBLE = 6, CONSTANT_CLASS = 7, CONSTANT_STRING = 8, CONSTANT_FIELD = 9, CONSTANT_METHOD = 10, CONSTANT_INTERFACEMETHOD = 11, CONSTANT_NAMEANDTYPE = 12, CONSTANT_METHODHANDLE = 15, CONSTANT_METHODTYPE = 16, CONSTANT_INVOKEDYNAMIC = 18 }; static char *opcNames[] = { NTXT ("nop"), NTXT ("aconst_null"), NTXT ("iconst_m1"), NTXT ("iconst_0"), NTXT ("iconst_1"), NTXT ("iconst_2"), NTXT ("iconst_3"), NTXT ("iconst_4"), NTXT ("iconst_5"), NTXT ("lconst_0"), NTXT ("lconst_1"), NTXT ("fconst_0"), NTXT ("fconst_1"), NTXT ("fconst_2"), NTXT ("dconst_0"), NTXT ("dconst_1"), NTXT ("bipush"), NTXT ("sipush"), NTXT ("ldc"), NTXT ("ldc_w"), NTXT ("ldc2_w"), NTXT ("iload"), NTXT ("lload"), NTXT ("fload"), NTXT ("dload"), NTXT ("aload"), NTXT ("iload_0"), NTXT ("iload_1"), NTXT ("iload_2"), NTXT ("iload_3"), NTXT ("lload_0"), NTXT ("lload_1"), NTXT ("lload_2"), NTXT ("lload_3"), NTXT ("fload_0"), NTXT ("fload_1"), NTXT ("fload_2"), NTXT ("fload_3"), NTXT ("dload_0"), NTXT ("dload_1"), NTXT ("dload_2"), NTXT ("dload_3"), NTXT ("aload_0"), NTXT ("aload_1"), NTXT ("aload_2"), NTXT ("aload_3"), NTXT ("iaload"), NTXT ("laload"), NTXT ("faload"), NTXT ("daload"), NTXT ("aaload"), NTXT ("baload"), NTXT ("caload"), NTXT ("saload"), NTXT ("istore"), NTXT ("lstore"), NTXT ("fstore"), NTXT ("dstore"), NTXT ("astore"), NTXT ("istore_0"), NTXT ("istore_1"), NTXT ("istore_2"), NTXT ("istore_3"), NTXT ("lstore_0"), NTXT ("lstore_1"), NTXT ("lstore_2"), NTXT ("lstore_3"), NTXT ("fstore_0"), NTXT ("fstore_1"), NTXT ("fstore_2"), NTXT ("fstore_3"), NTXT ("dstore_0"), NTXT ("dstore_1"), NTXT ("dstore_2"), NTXT ("dstore_3"), NTXT ("astore_0"), NTXT ("astore_1"), NTXT ("astore_2"), NTXT ("astore_3"), NTXT ("iastore"), NTXT ("lastore"), NTXT ("fastore"), NTXT ("dastore"), NTXT ("aastore"), NTXT ("bastore"), NTXT ("castore"), NTXT ("sastore"), NTXT ("pop"), NTXT ("pop2"), NTXT ("dup"), NTXT ("dup_x1"), NTXT ("dup_x2"), NTXT ("dup2"), NTXT ("dup2_x1"), NTXT ("dup2_x2"), NTXT ("swap"), NTXT ("iadd"), NTXT ("ladd"), NTXT ("fadd"), NTXT ("dadd"), NTXT ("isub"), NTXT ("lsub"), NTXT ("fsub"), NTXT ("dsub"), NTXT ("imul"), NTXT ("lmul"), NTXT ("fmul"), NTXT ("dmul"), NTXT ("idiv"), NTXT ("ldiv"), NTXT ("fdiv"), NTXT ("ddiv"), NTXT ("irem"), NTXT ("lrem"), NTXT ("frem"), NTXT ("drem"), NTXT ("ineg"), NTXT ("lneg"), NTXT ("fneg"), NTXT ("dneg"), NTXT ("ishl"), NTXT ("lshl"), NTXT ("ishr"), NTXT ("lshr"), NTXT ("iushr"), NTXT ("lushr"), NTXT ("iand"), NTXT ("land"), NTXT ("ior"), NTXT ("lor"), NTXT ("ixor"), NTXT ("lxor"), NTXT ("iinc"), NTXT ("i2l"), NTXT ("i2f"), NTXT ("i2d"), NTXT ("l2i"), NTXT ("l2f"), NTXT ("l2d"), NTXT ("f2i"), NTXT ("f2l"), NTXT ("f2d"), NTXT ("d2i"), NTXT ("d2l"), NTXT ("d2f"), NTXT ("i2b"), NTXT ("i2c"), NTXT ("i2s"), NTXT ("lcmp"), NTXT ("fcmpl"), NTXT ("fcmpg"), NTXT ("dcmpl"), NTXT ("dcmpg"), NTXT ("ifeq"), NTXT ("ifne"), NTXT ("iflt"), NTXT ("ifge"), NTXT ("ifgt"), NTXT ("ifle"), NTXT ("if_icmpeq"), NTXT ("if_icmpne"), NTXT ("if_icmplt"), NTXT ("if_icmpge"), NTXT ("if_icmpgt"), NTXT ("if_icmple"), NTXT ("if_acmpeq"), NTXT ("if_acmpne"), NTXT ("goto"), NTXT ("jsr"), NTXT ("ret"), NTXT ("tableswitch"), NTXT ("lookupswitch"), NTXT ("ireturn"), NTXT ("lreturn"), NTXT ("freturn"), NTXT ("dreturn"), NTXT ("areturn"), NTXT ("return"), NTXT ("getstatic"), NTXT ("putstatic"), NTXT ("getfield"), NTXT ("putfield"), NTXT ("invokevirtual"), NTXT ("invokespecial"), NTXT ("invokestatic"), NTXT ("invokeinterface"), NTXT ("invokedynamic"), NTXT ("new"), NTXT ("newarray"), NTXT ("anewarray"), NTXT ("arraylength"), NTXT ("athrow"), NTXT ("checkcast"), NTXT ("instanceof"), NTXT ("monitorenter"), NTXT ("monitorexit"), NTXT ("wide"), NTXT ("multianewarray"), NTXT ("ifnull"), NTXT ("ifnonnull"), NTXT ("goto_w"), NTXT ("jsr_w"), NTXT ("breakpoint") }; #define APPEND_FLAG(len, buf, flag, x) \ if (((x) & (flag)) != 0) \ { \ flag &= ~(x); \ AppendString(len, buf, NTXT("%s%s"), delimiter, #x); \ delimiter = NTXT("|"); \ } static char * access_flags_to_str (int kind, int flag) { static char buf[256]; size_t len = 0; buf[0] = 0; if (flag == 0) { AppendString (len, buf, NTXT ("0x%x"), (unsigned int) flag); return buf; } const char *delimiter = ""; if (kind == ClassAccess) { APPEND_FLAG (len, buf, flag, ACC_FINAL); APPEND_FLAG (len, buf, flag, ACC_SUPER); APPEND_FLAG (len, buf, flag, ACC_INTERFACE); APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); APPEND_FLAG (len, buf, flag, ACC_ENUM); if (flag) AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); } else if (kind == FieldAccess) { APPEND_FLAG (len, buf, flag, ACC_PUBLIC); APPEND_FLAG (len, buf, flag, ACC_PRIVATE); APPEND_FLAG (len, buf, flag, ACC_PROTECTED); APPEND_FLAG (len, buf, flag, ACC_STATIC); APPEND_FLAG (len, buf, flag, ACC_FINAL); APPEND_FLAG (len, buf, flag, ACC_VOLATILE); APPEND_FLAG (len, buf, flag, ACC_TRANSIENT); APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); APPEND_FLAG (len, buf, flag, ACC_ENUM); if (flag) AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); } else if (kind == MethodAccess) { APPEND_FLAG (len, buf, flag, ACC_PUBLIC); APPEND_FLAG (len, buf, flag, ACC_PRIVATE); APPEND_FLAG (len, buf, flag, ACC_PROTECTED); APPEND_FLAG (len, buf, flag, ACC_STATIC); APPEND_FLAG (len, buf, flag, ACC_FINAL); APPEND_FLAG (len, buf, flag, ACC_SYNCHRONIZED); APPEND_FLAG (len, buf, flag, ACC_BRIDGE); APPEND_FLAG (len, buf, flag, ACC_VARARGS); APPEND_FLAG (len, buf, flag, ACC_NATIVE); APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); APPEND_FLAG (len, buf, flag, ACC_STRICT); APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); if (flag) AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); } else if (kind == NestedClassAccess) { APPEND_FLAG (len, buf, flag, ACC_PUBLIC); APPEND_FLAG (len, buf, flag, ACC_PRIVATE); APPEND_FLAG (len, buf, flag, ACC_PROTECTED); APPEND_FLAG (len, buf, flag, ACC_STATIC); APPEND_FLAG (len, buf, flag, ACC_FINAL); APPEND_FLAG (len, buf, flag, ACC_INTERFACE); APPEND_FLAG (len, buf, flag, ACC_ABSTRACT); APPEND_FLAG (len, buf, flag, ACC_SYNTHETIC); APPEND_FLAG (len, buf, flag, ACC_ANNOTATION); APPEND_FLAG (len, buf, flag, ACC_ENUM); if (flag) AppendString (len, buf, "%s0x%x", delimiter, (unsigned int) (flag)); } return buf; } class DataReadException { public: DataReadException (char *s) { str_err = s; } ~DataReadException () { free (str_err); } char * toString () { return str_err; } private: char *str_err; }; class DataInputStream { public: DataInputStream (const unsigned char *bytes, int64_t sz) { bp = bp_orig = bytes; bp_last = bp_orig + sz; } DataInputStream (DataInputStream *in) { bp = bp_orig = in->bp_orig; bp_last = in->bp_last; } u1 readByte () { check (1); u1 val = *bp; bp++; return val; } u2 readUnsignedShort () { check (2); u2 val = (bp[0] << 8) | bp[1]; bp += 2; return val; } u4 readUnsigned () { check (4); u4 val = (bp[0] << 24) | (bp[1] << 16) | (bp[2] << 8) | bp[3]; bp += 4; return val; } const u1 * getptr () { return bp; } const size_t get_offset () { return bp - bp_orig; } void skip (int n) { check (n); bp += n; } void reset () { bp = bp_orig; } void copy_bytes (char *buf, int64_t len) { check (len); memcpy (buf, bp, len); buf[len] = '\0'; } private: void check (int64_t sz) { if (sz < 0 || bp + sz > bp_last) { DataReadException *e1 = new DataReadException ( dbe_sprintf (GTXT ("(Cannot read %lld byte(s) offset=0x%llx)\n"), (long long) sz, (long long) get_offset ())); throw (e1); } }; const unsigned char *bp_last; const unsigned char *bp_orig; const unsigned char *bp; }; class BinaryConstantPool { public: BinaryConstantPool (DataInputStream &in); ~BinaryConstantPool (); u1 getType (int n) { return (n < nconst && n > 0) ? types[n] : 0; }; char *getString (int index); private: static char *getTypeName (int ty); static char *type_name_to_str (int ty); static char *offset_to_str (long long offset); int nconst; u1 *types; int64_t *offsets; char **strings; DataInputStream *input; }; char * BinaryConstantPool::type_name_to_str (int ty) { static char buf[128]; char *tyName = getTypeName (ty); snprintf (buf, sizeof (buf), NTXT ("%s(%d)"), tyName, ty); return buf; } char * BinaryConstantPool::offset_to_str (long long offset) { static char buf[128]; snprintf (buf, sizeof (buf), NTXT ("offset=0x%06llx (%llu)"), offset, offset); return buf; } BinaryConstantPool::BinaryConstantPool (DataInputStream &in) { nconst = 0; types = NULL; offsets = NULL; strings = NULL; input = new DataInputStream (in); int cntConst = in.readUnsignedShort (); if (cntConst > 0) { types = new u1[cntConst]; types[0] = 0; offsets = new int64_t [cntConst]; strings = new char * [cntConst]; strings[0] = NULL; } Dprintf (DUMP_JAVA_CLASS, NTXT ("# BinaryConstantPool: %d\n"), (int) nconst); for (int i = 1; i < cntConst; i++) { nconst = i + 1; strings[i] = NULL; types[i] = in.readByte (); offsets[i] = in.get_offset (); Dprintf (DUMP_JAVA_CLASS, NTXT (" %3d %-25s %-25s"), i, offset_to_str (offsets[i]), type_name_to_str (types[i])); switch (types[i]) { case CONSTANT_UTF8: { u2 length = in.readUnsignedShort (); in.skip (length); Dprintf (DUMP_JAVA_CLASS, " length=%u\n", (unsigned int) length); break; } case CONSTANT_INTEGER: { u4 bytes = in.readUnsigned (); Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); break; } case CONSTANT_FLOAT: { u4 bytes = in.readUnsigned (); Dprintf (DUMP_JAVA_CLASS, " bytes=0x%08x\n", (unsigned int) bytes); break; } case CONSTANT_LONG: case CONSTANT_DOUBLE: { // JVM 4.4.5: all 8-byte constants take up // two entries in the constant_pool table. i++; nconst++; offsets[i] = 0; strings[i] = NULL; u4 high_bytes = in.readUnsigned (); u4 low_bytes = in.readUnsigned (); Dprintf (DUMP_JAVA_CLASS, NTXT (" high_bytes=0x%08x low_bytes=0x%08x\n"), (unsigned int) high_bytes, (unsigned int) low_bytes); break; } case CONSTANT_CLASS: { u2 name_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" name_index=%6u\n"), (unsigned int) name_index); break; } case CONSTANT_STRING: { u2 string_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" string_index=%4u\n"), (unsigned int) string_index); break; } case CONSTANT_FIELD: case CONSTANT_METHOD: case CONSTANT_INTERFACEMETHOD: { u2 class_index = in.readUnsignedShort (); u2 name_and_type_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" class_index=%5u name_and_type_index=%u\n"), (unsigned int) class_index, (unsigned int) name_and_type_index); break; } case CONSTANT_NAMEANDTYPE: { u2 name_index = in.readUnsignedShort (); u2 descriptor_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, " name_index=%6u descriptor_index=%u\n", (unsigned int) name_index, (unsigned int) descriptor_index); break; } case CONSTANT_METHODHANDLE: { u1 reference_kind = in.readByte (); u2 reference_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, " reference_kind=%u reference_index=%u\n", (unsigned int) reference_kind, (unsigned int) reference_index); break; } case CONSTANT_METHODTYPE: { u2 descriptor_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" descriptor_index=%u\n"), (unsigned int) descriptor_index); break; } case CONSTANT_INVOKEDYNAMIC: { u2 bootstrap_method_attr_index = in.readUnsignedShort (); u2 name_and_type_index = in.readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" bootstrap_method_attr_index=%5u name_and_type_index=%u\n"), (unsigned int) bootstrap_method_attr_index, (unsigned int) name_and_type_index); break; } default: Dprintf (DUMP_JAVA_CLASS, NTXT ("\n")); DataReadException *e1 = new DataReadException ( dbe_sprintf (GTXT ("BinaryConstantPool[%d]: bad tag %d %s\n"), i, types[i], offset_to_str (offsets[i]))); throw (e1); } } } BinaryConstantPool::~BinaryConstantPool () { delete[] types; delete[] offsets; delete input; if (strings) { for (int i = 0; i < nconst; i++) free (strings[i]); delete[] strings; } } #define CASE_S(x) case x: return (char *) #x char * BinaryConstantPool::getTypeName (int ty) { switch (ty) { CASE_S (CONSTANT_UTF8); CASE_S (CONSTANT_INTEGER); CASE_S (CONSTANT_FLOAT); CASE_S (CONSTANT_LONG); CASE_S (CONSTANT_DOUBLE); CASE_S (CONSTANT_CLASS); CASE_S (CONSTANT_STRING); CASE_S (CONSTANT_FIELD); CASE_S (CONSTANT_METHOD); CASE_S (CONSTANT_INTERFACEMETHOD); CASE_S (CONSTANT_NAMEANDTYPE); CASE_S (CONSTANT_METHODHANDLE); CASE_S (CONSTANT_METHODTYPE); CASE_S (CONSTANT_INVOKEDYNAMIC); default: return NTXT ("UNKNOWN_TYPE"); } } char * BinaryConstantPool::getString (int index) { if (index >= nconst || index <= 0) return NULL; if (strings[index]) return strings[index]; input->reset (); input->skip (offsets[index]); switch (types[index]) { case CONSTANT_CLASS: case CONSTANT_STRING: case CONSTANT_NAMEANDTYPE: strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); return strings[index]; case CONSTANT_METHOD: input->readUnsignedShort (); // cl_inx strings[index] = dbe_strdup (getString (input->readUnsignedShort ())); return strings[index]; case CONSTANT_UTF8: break; default: return NULL; } u2 len = input->readUnsignedShort (); strings[index] = (char *) xmalloc (len + 1); input->copy_bytes (strings[index], len); return strings[index]; } ClassFile::ClassFile () : Module () { input = NULL; bcpool = NULL; cf_buf = NULL; cur_jmthd = NULL; blanksCnt = 0; cf_bufsz = 0; lang_code = Sp_lang_java; class_name = NULL; class_filename = NULL; source_name = NULL; byteCodeInfo = NULL; } char * ClassFile::get_opc_name (int op) { if (op >= 0 && ((size_t) op) < sizeof (opcNames) / sizeof (char*)) return opcNames[op]; switch (op) { case opc_try: return NTXT ("try"); case opc_dead: return NTXT ("dead"); case opc_label: return NTXT ("label"); default: return NTXT ("Unknown op code"); } } void ClassFile::openFile (const char *fname) { if (fname == NULL) return; int fd = open64 (fname, O_RDONLY); if (fd == -1) { append_msg (CMSG_ERROR, GTXT ("Cannot open file %s"), fname); return; } dbe_stat_t stat_buf; if ((fstat64 (fd, &stat_buf) == -1) || (stat_buf.st_size == 0)) { close (fd); append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); return; } cf_bufsz = stat_buf.st_size; cf_buf = (unsigned char *) xmalloc (cf_bufsz); if (cf_bufsz != read_from_file (fd, cf_buf, cf_bufsz)) { free (cf_buf); cf_buf = NULL; close (fd); append_msg (CMSG_ERROR, GTXT ("Cannot read file %s"), fname); return; } close (fd); input = new DataInputStream (cf_buf, cf_bufsz); u4 c_magic = input->readUnsigned (); if (c_magic != JAVA_MAGIC) { append_msg (CMSG_ERROR, GTXT ("Not a class file: %s"), fname); return; } /* u2 minor = */ input->readUnsignedShort (); /* u2 major = */ input->readUnsignedShort (); status = AE_OK; } ClassFile::~ClassFile () { free (cf_buf); free (class_name); free (class_filename); free (source_name); delete bcpool; delete input; } static void convertName (char *s) { while (*s) { if (*s == '/') *s = '.'; s++; } } void ClassFile::printConstant (StringBuilder *sb, int index) { u1 type = bcpool->getType (index); switch (type) { case CONSTANT_METHOD: { char *str = bcpool->getString (index); if (str) { convertName (str); sb->append (str); sb->append (NTXT ("()")); } break; } case CONSTANT_CLASS: { char *str = bcpool->getString (index); if (str) { convertName (str); sb->append (str); } break; } case CONSTANT_UTF8: { char *str = bcpool->getString (index); if (str) sb->append (str); break; } case CONSTANT_STRING: { char *str = bcpool->getString (index); if (str) { sb->append ('"'); sb->append (str); sb->append ('"'); } break; } default: sb->append ('#'); sb->append ((int) index); break; } } long long ClassFile::printCodeSequence (StringBuilder *sb, uint64_t addr, DataInputStream *in) { int64_t offset = in->get_offset (); sb->appendf (NTXT ("%08llx: "), (long long) addr); int opcode = in->readByte (); if (opcode == opc_wide) { opcode = in->readByte (); sb->append (get_opc_name (opcode)); sb->append (NTXT ("_w ")); int arg = in->readUnsignedShort (); switch (opcode) { case opc_aload: case opc_astore: case opc_fload: case opc_fstore: case opc_iload: case opc_istore: case opc_lload: case opc_lstore: case opc_dload: case opc_dstore: case opc_ret: sb->append (arg); break; case opc_iinc: sb->append (arg); sb->append (' '); sb->append (in->readUnsignedShort ()); break; default: sb->append (GTXT ("Invalid opcode")); break; } } else { sb->append (get_opc_name (opcode)); sb->append (' '); switch (opcode) { case opc_aload: case opc_astore: case opc_fload: case opc_fstore: case opc_iload: case opc_istore: case opc_lload: case opc_lstore: case opc_dload: case opc_dstore: case opc_ret: sb->append (in->readByte ()); break; case opc_iinc: sb->append (in->readByte ()); sb->append (' '); sb->append (in->readByte ()); break; case opc_tableswitch: { int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch if (align != 0) { in->skip (4 - align); // four byte boundry } long default_skip = in->readUnsigned (); long low = in->readUnsigned (); long high = in->readUnsigned (); sb->appendf (GTXT ("%ld to %ld: default=0x%llx"), (long) low, (long) high, (long long) (addr + default_skip)); for (long i = low; i <= high; ++i) /* u4 i1 = */ in->readUnsigned (); break; } case opc_lookupswitch: { int align = (addr + 1) % 4; // 1 byte is a length of opc_lookupswitch if (align != 0) in->skip (4 - align); // four byte boundry u4 default_skip = in->readUnsigned (); u4 npairs = in->readUnsigned (); sb->appendf (GTXT ("%d: default=0x%llx"), npairs, (long long) (addr + default_skip)); for (int i = 0, nints = npairs * 2; i < nints; i += 2) { /* u4 i1 = */ in->readUnsigned (); /* u4 i2 = */ in->readUnsigned (); } break; } case opc_newarray: switch (in->readByte ()) { case T_INT: sb->append (GTXT ("int")); break; case T_LONG: sb->append (GTXT ("long")); break; case T_FLOAT: sb->append (GTXT ("float")); break; case T_DOUBLE: sb->append (GTXT ("double")); break; case T_CHAR: sb->append (GTXT ("char")); break; case T_SHORT: sb->append (GTXT ("short")); break; case T_BYTE: sb->append (GTXT ("byte")); break; case T_BOOLEAN: sb->append (GTXT ("boolean")); break; default: sb->append (GTXT ("BOGUS TYPE")); break; } break; case opc_anewarray: sb->append (GTXT ("class ")); printConstant (sb, in->readUnsignedShort ()); break; case opc_sipush: sb->append (in->readUnsignedShort ()); break; case opc_bipush: sb->append (in->readByte ()); break; case opc_ldc: printConstant (sb, in->readByte ()); break; case opc_ldc_w: case opc_ldc2_w: case opc_instanceof: case opc_checkcast: case opc_new: case opc_putstatic: case opc_getstatic: case opc_putfield: case opc_getfield: case opc_invokevirtual: case opc_invokespecial: case opc_invokestatic: printConstant (sb, in->readUnsignedShort ()); break; case opc_invokeinterface: { u2 index = in->readUnsignedShort (); u1 count = in->readByte (); /* u1 zero = */ in->readByte (); sb->appendf (" #%u, %u) ", (unsigned int) index, (unsigned int) count); printConstant (sb, index); break; } case opc_multianewarray: { u2 index = in->readUnsignedShort (); printConstant (sb, index); sb->appendf (GTXT (" dim #%d "), index); break; } case opc_jsr: case opc_goto: case opc_ifeq: case opc_ifge: case opc_ifgt: case opc_ifle: case opc_iflt: case opc_ifne: case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge: case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt: case opc_if_acmpeq: case opc_if_acmpne: case opc_ifnull: case opc_ifnonnull: sb->appendf (NTXT ("0x%llx"), (long long) (addr + (short) in->readUnsignedShort ())); break; case opc_jsr_w: case opc_goto_w: sb->append (addr + (int) in->readUnsigned ()); break; default: break; } } return in->get_offset () - offset; } void ClassFile::readAttributes (int count) { blanksCnt += 4; for (int ax = 0; ax < count; ax++) { u2 attribute_name_index = input->readUnsignedShort (); u4 attribute_length = input->readUnsigned (); char *attribute_name = bcpool->getString (attribute_name_index); if (!attribute_name) { Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length); input->skip (attribute_length); continue; } if (strcmp (attribute_name, NTXT ("SourceFile")) == 0) { u2 sourcefile_index = input->readUnsignedShort (); source_name = dbe_strdup (bcpool->getString (sourcefile_index)); Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d file_name=%d %s\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length, (int) sourcefile_index, STR (source_name)); } else if (strcmp (attribute_name, NTXT ("InnerClasses")) == 0) { int niclasses = input->readUnsignedShort (); for (int ix = 0; ix < niclasses; ix++) { u2 inner_class_info_index = input->readUnsignedShort (); u2 outer_class_info_index = input->readUnsignedShort (); u2 inner_name_index = input->readUnsignedShort (); u2 inner_class_access_flags = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d name=%d '%s'\n" "%*cinner_class_info_index=%d outer_class_info_index=%d flags=%s\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length, (int) inner_name_index, STR (bcpool->getString (inner_name_index)), (int) (blanksCnt + 10), ' ', (int) inner_class_info_index, (int) outer_class_info_index, access_flags_to_str (NestedClassAccess, inner_class_access_flags)); } } else if (strcmp (attribute_name, NTXT ("Code")) == 0) { u2 max_stack = input->readUnsignedShort (); u2 max_locals = input->readUnsignedShort (); u4 code_length = input->readUnsigned (); if (cur_jmthd) { cur_jmthd->size = code_length; cur_jmthd->img_fname = dbeFile->get_location (); cur_jmthd->img_offset = input->get_offset (); } input->skip (code_length); u2 exception_table_length = input->readUnsignedShort (); input->skip (exception_table_length * (2 + 2 + 2 + 2)); Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d max_stack=%d max_locals=%d code_length=%d exception_table_length=%d\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length, (int) max_stack, (int) max_locals, (int) code_length, (int) exception_table_length); readAttributes (input->readUnsignedShort ()); } else if (strcmp (attribute_name, NTXT ("LineNumberTable")) == 0) { int nlines = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d nlines=%d\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length, (int) nlines); for (int lx = 0; lx < nlines; lx++) { int bci = input->readUnsignedShort (); int lno = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %3d: pc=%4d (0x%04x) line=%d\n"), (int) (blanksCnt + 5), ' ', (int) (lx + 1), (int) bci, (int) bci, (int) lno); if (cur_jmthd) byteCodeInfo->append (new ByteCodeInfo (cur_jmthd, bci, lno)); } } else { Dprintf (DUMP_JAVA_CLASS, NTXT ("%*c %2d: attr_name=%3d %-15s len=%4d\n"), (int) blanksCnt, ' ', (int) (ax + 1), (int) attribute_name_index, STR (attribute_name), (int) attribute_length); input->skip (attribute_length); } } blanksCnt -= 4; } int ClassFile::readFile () { if (status != AE_NOTREAD) return status; status = AE_OTHER; // The ClassFile Structure http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html try { blanksCnt = 4; cur_jmthd = NULL; char *fname = dbeFile->get_location (); openFile (fname); Dprintf (DUMP_JAVA_CLASS, NTXT ("\nClassFile::readFile status=%d %s location=%s\n"), (unsigned int) status, STR (get_name ()), STR (fname)); if (status != AE_OK) return status; byteCodeInfo = new Vector(512); bcpool = new BinaryConstantPool (*input); u2 access_flags = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT ("\naccess_flags=%s; %s\n"), access_flags_to_str (ClassAccess, access_flags), STR (dbeFile->get_name ())); u2 classNameInd = input->readUnsignedShort (); class_filename = dbe_strdup (bcpool->getString (classNameInd)); if (class_filename) { class_name = xstrdup (class_filename); convertName (class_name); } // Get superclass name u2 superClassInd = input->readUnsignedShort (); //char *str = bcpool->getString(superClassInd); //super_name = str ? convertName( str ) : NULL; // Read interfaces int interfaces_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" class_name=%3d %-20s superClass=%3d %s interfaces_count=%d\n"), (int) classNameInd, STR (class_name), (int) superClassInd, STR (bcpool->getString (superClassInd)), (int) interfaces_count); for (int i = 0; i < interfaces_count; i++) { u2 index = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" %6lld%s"), (long long) index, (i % 8 == 7) || (i + 1 == interfaces_count) ? "\n" : ""); } // Read fields int fields_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" fields_count=%d\n"), fields_count); for (int i = 0; i < fields_count; i++) { u2 fld_access_flags = input->readUnsignedShort (); u2 name_index = input->readUnsignedShort (); u2 descriptor_index = input->readUnsignedShort (); u2 attributes_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" %2d: name=%3d %-20s flags=%s; desc_ind=%d attr_count=%d\n"), i, (int) name_index, STR (bcpool->getString (name_index)), access_flags_to_str (FieldAccess, fld_access_flags), (int) descriptor_index, (int) attributes_count); readAttributes (attributes_count); } // Read methods int methods_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT ("\n methods_count=%d\n"), (int) methods_count); int func_cnt = functions->size (); for (int i = 0; i < methods_count; i++) { u2 mthd_access_flags = input->readUnsignedShort (); u2 name_index = input->readUnsignedShort (); u2 descriptor_index = input->readUnsignedShort (); char *mname = bcpool->getString (name_index); if (mname == NULL) { DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method name[%d] is NULL\n"), i)); throw (e1); } char *msign = bcpool->getString (descriptor_index); if (msign == NULL) { DataReadException *e1 = new DataReadException (dbe_sprintf (GTXT ("method signature[%d] is NULL\n"), i)); throw (e1); } size_t len = strlen (class_name); cur_jmthd = NULL; for (int idx = 0; idx < func_cnt; idx++) { JMethod *jmthd = (JMethod*) functions->fetch (idx); char *jmt_name = jmthd->get_name (Histable::SHORT); if (strncmp (jmt_name, class_name, len) == 0) { if (strcmp (jmt_name + len + 1, mname) == 0 && strcmp (jmthd->get_signature (), msign) == 0) { cur_jmthd = jmthd; break; } } } if (cur_jmthd == NULL) { cur_jmthd = dbeSession->createJMethod (); cur_jmthd->module = this; cur_jmthd->set_signature (dbe_strdup (msign)); char *nm = dbe_sprintf (NTXT ("%s.%s"), class_name, mname); cur_jmthd->set_name (nm); free (nm); functions->append (cur_jmthd); } if ((mthd_access_flags & ACC_NATIVE) != 0) { cur_jmthd->flags |= FUNC_FLAG_NATIVE; } u2 attributes_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" %2d: name=%d %-20s flags=%s desc_ind=%d attr_count=%d\n"), (int) (i + 1), (int) name_index, STR (bcpool->getString (name_index)), access_flags_to_str (MethodAccess, mthd_access_flags), (int) descriptor_index, (int) attributes_count); readAttributes (attributes_count); cur_jmthd->popSrcFile (); } // Read global attributes u2 global_attributes_count = input->readUnsignedShort (); Dprintf (DUMP_JAVA_CLASS, NTXT (" global_attributes_count=%d\n"), global_attributes_count); readAttributes (global_attributes_count); status = AE_OK; } catch (DataReadException *ex) { append_msg (CMSG_ERROR, GTXT ("Cannot read class file %s (%s)"), get_name (), ex->toString ()); delete ex; status = AE_OTHER; } char *fnm = NULL; if (class_filename) { if (strcmp (class_filename, get_name ()) != 0) set_name (xstrdup (class_filename)); if (source_name) { char *bname = strrchr (class_filename, '/'); if (bname) fnm = dbe_sprintf (NTXT ("%.*s/%s"), (int) (bname - class_filename), class_filename, source_name); else fnm = xstrdup (source_name); } else fnm = get_java_file_name (class_filename, false); } else if (source_name) fnm = xstrdup (source_name); if (fnm) { set_file_name (fnm); main_source = findSource (file_name, true); main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; } for (long i = 0, sz = VecSize (functions); i < sz; i++) functions->get (i)->def_source = main_source; JMethod *func = NULL; for (long i = 0, sz = VecSize (byteCodeInfo); i < sz; i++) { ByteCodeInfo *p = byteCodeInfo->get (i); if (func != p->func) { if (func) func->popSrcFile (); func = p->func; func->line_first = p->lno; func->pushSrcFile (main_source, 0); } func->line_last = p->lno; func->add_PC_info (p->bci, p->lno, main_source); } if (func) func->popSrcFile (); Destroy (byteCodeInfo); Dprintf (DUMP_JAVA_CLASS, NTXT ("\n status=%d class_filename=%s class_name=%s source_name=%s file_name=%s %s\n"), (unsigned int) status, STR (class_filename), STR (class_name), STR (source_name), STR (file_name), STR (get_name ())); return status; } #define MAX_CLASS_SIZE 65536 char * ClassFile::get_disasm (uint64_t inst_address, uint64_t end_address, uint64_t start_address, uint64_t f_offset, int64_t &inst_size) { int64_t offset = f_offset + (inst_address - start_address); if ((cf_buf == NULL) || (inst_address >= end_address) || (offset >= cf_bufsz)) { inst_size = 0; return NULL; } // Check for an implausibly large size if ((inst_address - start_address) > MAX_CLASS_SIZE) { append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s), implausible size = %lld"), get_name (), dbeFile->get_location (), (long long) (end_address - start_address)); inst_size = 0; return NULL; } StringBuilder sb; DataInputStream *in = new DataInputStream (input); try { in->skip (offset); inst_size = printCodeSequence (&sb, inst_address - start_address, in); } catch (DataReadException *ex) { append_msg (CMSG_ERROR, GTXT ("Cannot disassemble class file %s (%s) %s"), get_name (), dbeFile->get_location (), ex->toString ()); delete ex; inst_size = 0; } delete in; if (inst_size == 0) return NULL; return sb.toString (); } char * ClassFile::get_java_file_name (char *clname, bool classSuffix) { size_t len = strlen (clname); if (len > 6 && streq (clname + len - 6, NTXT (".class"))) len -= 6; if (!classSuffix) { // remove $SubClassName from "ClassName$SubClassName" char *tmp = strchr (clname, '$'); if (tmp) len = tmp - clname; } char *clpath = (char *) xmalloc (len + 10); for (size_t i = 0; i < len; i++) clpath[i] = (clname[i] == '.') ? '/' : clname[i]; snprintf (clpath + len, 10, classSuffix ? NTXT (".class") : NTXT (".java")); return clpath; }