2500 lines
70 KiB
C++
2500 lines
70 KiB
C++
/*
|
|
* Interactive disassembler (IDA)
|
|
* Copyright (c) 1990-98 by Ilfak Guilfanov.
|
|
* E-mail: ig@datarescue.com
|
|
* JVM module.
|
|
* Copyright (c) 1995-2006 by Iouri Kharon.
|
|
* E-mail: yjh@styx.cabel.net
|
|
*
|
|
* ALL RIGHTS RESERVED.
|
|
*
|
|
*/
|
|
#include "java.hpp"
|
|
#include <loader.hpp>
|
|
#include <diskio.hpp>
|
|
#include <segregs.hpp>
|
|
#include "npooluti.hpp"
|
|
#include "oututil.hpp"
|
|
|
|
#define _CUR_IDP_VER IDP_JDK16
|
|
|
|
static const char constant_pool[] = "$ Constant Pool ~";
|
|
|
|
//-------------------------------------------------------------------------
|
|
nodeidx_t bootstrap_methods_get_node(bool assert, bool can_create)
|
|
{
|
|
// if ( curClass.bmNode != 0 )
|
|
// return curClass.bmNode;
|
|
netnode n("$ BootstrapMethods ~");
|
|
if ( can_create && !netnode_exist(n) )
|
|
n.create("$ BootstrapMethods ~");
|
|
if ( assert )
|
|
QASSERT(10333, netnode_exist(n));
|
|
return n;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
nodeidx_t bootstrap_methods_get_count()
|
|
{
|
|
return netnode(bootstrap_methods_get_node()).altval(0);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void bootstrap_methods_set_count(nodeidx_t cnt)
|
|
{
|
|
netnode(bootstrap_methods_get_node()).altset(0, cnt);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool bootstrap_methods_get_method(
|
|
bootstrap_method_def_t *out,
|
|
nodeidx_t idx)
|
|
{
|
|
netnode n = bootstrap_methods_get_node();
|
|
bytevec_t blob;
|
|
if ( n.getblob(&blob, idx, stag) < 2 )
|
|
return false;
|
|
const uchar *p = blob.begin();
|
|
out->method_ref = *(ushort *) p;
|
|
p += sizeof(ushort);
|
|
const size_t nargs = size_t(blob.end() - p) / sizeof(ushort);
|
|
out->args.resize(nargs);
|
|
if ( nargs > 0 )
|
|
memcpy(out->args.begin(), p, nargs * sizeof(ushort));
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
static bool bootstrap_methods_set_method(
|
|
const bootstrap_method_def_t &bmd,
|
|
nodeidx_t idx)
|
|
{
|
|
netnode n = bootstrap_methods_get_node();
|
|
bytevec_t blob;
|
|
blob.append(&bmd.method_ref, sizeof(bmd.method_ref));
|
|
if ( !bmd.args.empty() )
|
|
blob.append(bmd.args.begin(), bmd.args.size() * sizeof(ushort));
|
|
return n.setblob(blob.begin(), blob.size(), idx, stag);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void java_t::database_loaded(const char *file)
|
|
{
|
|
int i, v;
|
|
ssize_t size;
|
|
|
|
if ( file == NULL ) // load old file
|
|
{
|
|
if ( ConstantNode.create(constant_pool) )
|
|
goto BADIDB;
|
|
|
|
i = sizeof(curClass);
|
|
v = (int)ConstantNode.altval(CNA_VERSION);
|
|
switch ( v )
|
|
{
|
|
default:
|
|
error("Very old database format. Cannot convert it");
|
|
|
|
case IDP_JDK15:
|
|
error("Intermediate (developer) database format. Cannot convert it.");
|
|
|
|
case IDP_JDK12:
|
|
i -= sizeof(curClass) - offsetof(ClassInfo, MajVers);
|
|
// no break
|
|
case _CUR_IDP_VER: // IDP_JDK16
|
|
break;
|
|
}
|
|
|
|
if ( i != (int)ConstantNode.supval(CNS_CLASS, NULL, (size_t)-1) )
|
|
goto BADIDB;
|
|
size = ConstantNode.supval(CNS_CLASS, &curClass, sizeof(curClass));
|
|
if ( size < (int32)offsetof(ClassInfo, MajVers) || !curClass.ClassNode )
|
|
goto BADIDB;
|
|
if ( curClass.xtrnNode )
|
|
{
|
|
if ( !curClass.xtrnCnt )
|
|
goto BADIDB;
|
|
XtrnNode = curClass.xtrnNode;
|
|
}
|
|
else if ( curClass.xtrnCnt )
|
|
{
|
|
goto BADIDB;
|
|
}
|
|
ClassNode = curClass.ClassNode;
|
|
|
|
if ( v != IDP_JDK12 ) // current JDK format
|
|
{
|
|
if ( curClass.MajVers < JDK_MIN_MAJOR )
|
|
goto BADIDB;
|
|
if ( curClass.MajVers > JDK_MAX_MAJOR )
|
|
goto BADIDB;
|
|
if ( curClass.MajVers == JDK_MIN_MAJOR ) // JDK1.0/1.1
|
|
{
|
|
if ( curClass.JDKsubver != (curClass.MinVers >= JDK_1_1_MINOR) )
|
|
goto BADIDB;
|
|
}
|
|
else
|
|
{
|
|
if ( curClass.MinVers )
|
|
goto BADIDB;
|
|
if ( curClass.MajVers - (JDK_MIN_MAJOR-1) != curClass.JDKsubver )
|
|
goto BADIDB;
|
|
}
|
|
}
|
|
|
|
make_NameChars(/*on_load=*/ false); // initialize and set enableExr_NameChar for upgrade
|
|
if ( v != _CUR_IDP_VER )
|
|
{
|
|
ResW_init(); // prepare RW-find
|
|
v = upgrade_db_format(v, ConstantNode);
|
|
if ( v == 0 )
|
|
{
|
|
BADIDB:
|
|
DESTROYED("database_loaded");
|
|
}
|
|
QASSERT(10134, v == _CUR_IDP_VER);
|
|
ConstantNode.supset(CNS_CLASS, &curClass, sizeof(curClass));
|
|
ConstantNode.altset(CNA_VERSION, _CUR_IDP_VER);
|
|
}
|
|
else
|
|
{
|
|
v = curClass.maxStrSz;
|
|
if ( v != 0 )
|
|
tsPtr = (ushort*)myAlloc(sizeof(ushort)*(v+1));
|
|
v = curClass.maxSMsize;
|
|
if ( v != 0 )
|
|
smBuf = (uchar*)myAlloc(v+1);
|
|
v = curClass.maxAnnSz;
|
|
if ( v != 0 )
|
|
annBuf = (uchar*)myAlloc(v+1);
|
|
idpflags = (ushort)ConstantNode.altval(CNA_IDPFLAGS);
|
|
user_limiter = (uchar)ConstantNode.altval(CNA_LIMITER);
|
|
|
|
if ( !ResW_oldbase() )
|
|
goto BADIDB;
|
|
}
|
|
// disableExt_NameChar(); // set standart extension
|
|
if ( curClass.extflg & XFL_C_DONE )
|
|
sm_node = smn_ok;
|
|
if ( curClass.MajVers >= JDK_SMF_MAJOR_MIN )
|
|
SMF_mode = 1;
|
|
}
|
|
else
|
|
{ // new base
|
|
if ( !ConstantNode )
|
|
INTERNAL("ConstantNode");
|
|
|
|
char str[MAXSPECSIZE];
|
|
char *ps = str;
|
|
i = qstrlen((char *) file);
|
|
if ( i >= sizeof(str) )
|
|
{
|
|
ps = (char *) file + i - (sizeof(str) - 3);
|
|
for ( i = sizeof(str) - 1 - 3; i; i-- )
|
|
if ( *--ps == '/' )
|
|
break;
|
|
if ( i == 0 )
|
|
error("notify: illegal file name parameter");
|
|
file = ps;
|
|
memcpy(str, "...", 3);
|
|
ps = &str[3];
|
|
}
|
|
memcpy(ps, file, i);
|
|
if ( ps != str )
|
|
i += 3;
|
|
ConstantNode.supset(CNS_SOURCE, str, i);
|
|
user_limiter = inf_get_limiter();
|
|
ConstantNode.altset(CNA_LIMITER, user_limiter);
|
|
}
|
|
inf_set_limiter(0);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
void java_t::dump_floating_constants(
|
|
const char *problem,
|
|
const char *what,
|
|
const intvec_t &ks)
|
|
{
|
|
const size_t kss = ks.size();
|
|
if ( kss > 0 )
|
|
{
|
|
static const char *const emc = "Number of %s CONSTANT_%s: %" FMT_Z "\n";
|
|
load_msg(emc, problem, what, kss);
|
|
for ( size_t i = 0; i < kss; ++i )
|
|
{
|
|
qstrvec_t lines;
|
|
|
|
const_desc_t cd;
|
|
ushort cid = ks[i];
|
|
if ( ConstantNode.supval(cid, &cd, sizeof(cd)) == sizeof(cd) )
|
|
{
|
|
print_constant(&lines, cd, cid);
|
|
if ( lines.empty() )
|
|
lines.push_back("<failed printing constant>");
|
|
}
|
|
else
|
|
{
|
|
cd.type = 0;
|
|
lines.push_back("<failed retrieving constant>");
|
|
}
|
|
for ( size_t lidx = 0, lcnt = lines.size(); lidx < lcnt; ++lidx )
|
|
{
|
|
char sfxbuf[MAXSTR];
|
|
if ( lcnt > 1 )
|
|
qsnprintf(sfxbuf, sizeof(sfxbuf), ".%" FMT_Z, lidx);
|
|
else
|
|
sfxbuf[0] = '\0';
|
|
load_msg(" #%" FMT_Z "%s, index=%d, type=%s: %s",
|
|
i, sfxbuf, cid, constant_type_to_str(cd.type),
|
|
lines[lidx].c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool java_t::LoadOpis(load_mode_t load_mode, ushort index, uchar _op, const_desc_t *p)
|
|
{
|
|
const uchar op = _op;
|
|
#define LoadAnyString(lmod, index) LoadOpis(lmod, index, CONSTANT_Utf8, NULL)
|
|
const_desc_t tmp;
|
|
if ( p == NULL )
|
|
p = &tmp;
|
|
|
|
if ( !index
|
|
|| index > curClass.maxCPindex
|
|
|| ConstantNode.supval(index, p, sizeof(*p)) != sizeof(*p) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( load_mode == lm_lenient || load_mode == lm_no_set_ref )
|
|
return true;
|
|
|
|
if ( !p->is_referenced() )
|
|
{
|
|
p->mark_referenced();
|
|
StoreOpis(index, *p);
|
|
}
|
|
CASSERT(MAX_CONSTANT_TYPE < 0x20);
|
|
if ( op < 0x20 )
|
|
return op == 0 || op == p->type;
|
|
|
|
#define LoadNamedClass(lmod, index, p) LoadOpis(lmod, index, uchar(-1), p)
|
|
if ( op == uchar(-1) )
|
|
return p->type == CONSTANT_Class && (p->flag & HAS_CLSNAME);
|
|
|
|
if ( p->type != CONSTANT_Utf8 )
|
|
return false;
|
|
|
|
#define OP_TYPE_MASK 0x1F
|
|
#define OP_CHECK_SHIFT 5
|
|
|
|
const uchar _type = op & OP_TYPE_MASK;
|
|
const uchar _check = op >> OP_CHECK_SHIFT;
|
|
switch ( _type )
|
|
{
|
|
default:
|
|
INTERNAL("LoadOpis");
|
|
case 0:
|
|
case CONSTANT_Utf8:
|
|
break;
|
|
}
|
|
{
|
|
static const uchar chk[8] =
|
|
{
|
|
0x00, // align
|
|
|
|
#define CheckAnyDscr(lmod, index, p) LoadOpis(lmod, index, (1 << OP_CHECK_SHIFT), p)
|
|
(HAS_TYPEDSCR | HAS_CALLDSCR),
|
|
|
|
#define CheckFieldDscr(lmod, index, p) LoadOpis(lmod, index, (2 << OP_CHECK_SHIFT), p)
|
|
#define LoadFieldDscr(lmod, index, p) LoadOpis(lmod, index, (2 << OP_CHECK_SHIFT) | CONSTANT_Utf8, p)
|
|
HAS_TYPEDSCR,
|
|
|
|
#define CheckFieldName(lmod, index, p) LoadOpis(lmod, index, (3 << OP_CHECK_SHIFT), p)
|
|
#define LoadFieldName(lmod, index) LoadOpis(lmod, index, (3 << OP_CHECK_SHIFT) | CONSTANT_Utf8, NULL)
|
|
HAS_FLDNAME,
|
|
|
|
//#define Check...(lmod, index, p) LoadOpis(lmod, index, (4 << OP_CHECK_SHIFT), p)
|
|
0x00,
|
|
|
|
#define CheckClass(lmod, index, p) LoadOpis(lmod, index, (5 << OP_CHECK_SHIFT), p)
|
|
(HAS_TYPEDSCR | HAS_CLSNAME),
|
|
|
|
#define CheckCallDscr(lmod, index, p) LoadOpis(lmod, index, (6 << OP_CHECK_SHIFT), p)
|
|
#define LoadCallDscr(lmod, index) LoadOpis(lmod, index, (6 << OP_CHECK_SHIFT) | CONSTANT_Utf8, NULL)
|
|
HAS_CALLDSCR,
|
|
|
|
#define CheckClassName(lmod, index, p) LoadOpis(lmod, index, (7 << OP_CHECK_SHIFT), p)
|
|
HAS_CLSNAME
|
|
};
|
|
|
|
if ( p->flag & chk[_check] )
|
|
return true;
|
|
}
|
|
|
|
if ( _type != 0 )
|
|
return false;
|
|
|
|
if ( load_mode == lm_need_cr )
|
|
msg("\n");
|
|
load_msg("Illegal reference type to Utf8#%u\n", index);
|
|
return lm_need_cr >= 2; // true when load_constants_pool, false after
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// for annotation
|
|
bool java_t::isSingleClass(ushort val)
|
|
{
|
|
const_desc_t co;
|
|
|
|
return LoadFieldDscr(lm_normal, val, &co)
|
|
&& (co._Sflags & (_OP_VALPOS | _OP_ONECLS)) == _OP_ONECLS;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
uchar java_t::attribute_type_from_str(ushort index, attr_parent_kind_t apk, char str[MAX_ATTR_NMSZ])
|
|
{
|
|
static const char *const name[] =
|
|
{
|
|
#define attr_LineNumberTable 0 // 0x000001: code
|
|
"LineNumberTable",
|
|
#define attr_LocalVariableTable 1 // 0x000002: code
|
|
"LocalVariableTable",
|
|
#define attr_LocalVariableTypeTable 2 // 0x000004: code
|
|
"LocalVariableTypeTable",
|
|
#define attr_StackMap 3 // 0x000008: code (J2ME CLDC)
|
|
"StackMap",
|
|
#define attr_StackMapTable 4 // 0x000010: code (>=JDK1.6)
|
|
"StackMapTable",
|
|
#define attr_CODE_TOP 5
|
|
|
|
#define attr_ConstantValue 5 // 0x000020: fld
|
|
"ConstantValue",
|
|
#define attr_FLD_TOP 6
|
|
|
|
#define attr_Code 6 // 0x000040: met
|
|
"Code",
|
|
#define attr_Exceptions 7 // 0x000080: met
|
|
"Exceptions",
|
|
#define attr_RuntimeVisibleParameterAnnotations 8 // 0x000100: met
|
|
"RuntimeVisibleParameterAnnotations",
|
|
#define attr_RuntimeInvisibleParameterAnnotations 9 // 0x000200: met
|
|
"RuntimeInvisibleParameterAnnotations",
|
|
#define attr_AnnotationDefault 10 // 0x000400: met
|
|
"AnnotationDefault",
|
|
#define attr_MET_TOP 11
|
|
|
|
#define attr_SourceFile 11 // 0x000800: file
|
|
"SourceFile",
|
|
#define attr_InnerClasses 12 // 0x001000: file
|
|
"InnerClasses",
|
|
#define attr_EnclosingMethod 13 // 0x002000: file
|
|
"EnclosingMethod",
|
|
#define attr_SourceDebugExtension 14 // 0x004000: file
|
|
"SourceDebugExtension",
|
|
#define attr_BootstrapMethods 15 // 0x008000: file
|
|
"BootstrapMethods",
|
|
#define attr_FILE_TOP 16
|
|
|
|
#define attr_Signature 16 // 0x010000: all !code
|
|
"Signature",
|
|
#define attr_Synthetic 17 // 0x020000: all !code
|
|
"Synthetic",
|
|
#define attr_Deprecated 18 // 0x040000: all !code
|
|
"Deprecated",
|
|
#define attr_RuntimeVisibleAnnotations 19 // 0x080000: all !code
|
|
"RuntimeVisibleAnnotations",
|
|
#define attr_RuntimeInvisibleAnnotations 20 // 0x100000: all !code
|
|
"RuntimeInvisibleAnnotations",
|
|
// next field for check pool ONLY (must be in last position)
|
|
#define attr_LocalVariables 21 // 0x200000: obsolete
|
|
"LocalVariables"
|
|
#define attr_CHECK_MASK 0x3FFFFF
|
|
};
|
|
#define attr_UNKNOWN 32
|
|
#define attr_TRUNCATED 33
|
|
#define attr_NONAME 34
|
|
//#if sizeof("RuntimeInvisibleParameterAnnotations") > MAX_ATTR_NMSZ
|
|
//#error
|
|
//#endif
|
|
|
|
str[0] = '\0';
|
|
if ( !LoadFieldName(lm_normal, index) )
|
|
return attr_NONAME;
|
|
if ( !fmtName(index, str, MAX_ATTR_NMSZ, fmt_UnqualifiedName) )
|
|
return attr_TRUNCATED;
|
|
|
|
uchar i = 0, top = (uchar)(qnumber(name) - 1);
|
|
switch ( apk )
|
|
{
|
|
case attr_parent_kind_code:
|
|
top = attr_CODE_TOP;
|
|
break;
|
|
case attr_parent_kind_field:
|
|
i = attr_CODE_TOP;
|
|
top = attr_FLD_TOP;
|
|
break;
|
|
case attr_parent_kind_method:
|
|
i = attr_FLD_TOP;
|
|
top = attr_MET_TOP;
|
|
break;
|
|
case attr_parent_kind_class_file:
|
|
i = attr_MET_TOP;
|
|
top = attr_FILE_TOP;
|
|
break;
|
|
default:
|
|
// case ARQ_CHECK:
|
|
++top;
|
|
break;
|
|
}
|
|
|
|
repeat:
|
|
do
|
|
{
|
|
if ( !strcmp(name[i], str) )
|
|
return i;
|
|
} while ( ++i < top );
|
|
if ( apk != attr_parent_kind_code && i < (uchar)(qnumber(name)-1) )
|
|
{
|
|
i = attr_FILE_TOP;
|
|
top = (uchar)(qnumber(name) - 1);
|
|
goto repeat;
|
|
}
|
|
return attr_UNKNOWN;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
static NORETURN void badref_loader_failure(ushort i, ushort k)
|
|
{
|
|
loader_failure("Bad reference (from %u to %u) in constant pool", i, k);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
uint java_t::load_constants_pool(void)
|
|
{
|
|
ushort k;
|
|
uint ui = 0;
|
|
const_desc_t cd;
|
|
|
|
// prepare jasmin reserved word checking
|
|
ResW_init();
|
|
|
|
msg("\nLoading constant pool...");
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
memset(&cd, 0, sizeof(cd));
|
|
cd.type = read1();
|
|
switch ( cd.type )
|
|
{
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Double:
|
|
cd.value2 = read4();
|
|
// fallthrough
|
|
case CONSTANT_Integer:
|
|
case CONSTANT_Float:
|
|
cd.value = read4();
|
|
break;
|
|
|
|
case CONSTANT_NameAndType:
|
|
case CONSTANT_Fieldref:
|
|
case CONSTANT_Methodref:
|
|
case CONSTANT_InterfaceMethodref:
|
|
k = read2();
|
|
if ( k == 0 || k > curClass.maxCPindex )
|
|
{
|
|
badindex:
|
|
loader_failure("Bad record in constant pool.\n"
|
|
"Record %u have reference to %u\n"
|
|
"(maxnum %u, file offset after the read is 0x%" FMT_64 "X)",
|
|
i, k, curClass.maxCPindex, qftell(myFile));
|
|
}
|
|
cd._class = k; // _subnam for name & type
|
|
// fallthrough. Yes, we will let 'name_and_type_index' be read by the CONSTANT_String block below...
|
|
case CONSTANT_Class:
|
|
cd.ref_ip = 0;
|
|
// fallthrough
|
|
case CONSTANT_String:
|
|
k = read2();
|
|
if ( k == 0 || k > curClass.maxCPindex )
|
|
goto badindex;
|
|
cd._name = k; // _dscr for name & type
|
|
break;
|
|
|
|
case CONSTANT_Unicode:
|
|
loader_failure("File contains CONSTANT_Unicode, which was removed from "
|
|
"the standard in 1996, and is not supported by IDA");
|
|
|
|
case CONSTANT_Utf8:
|
|
cd._name = (ushort)i; // for xtrnRef_dscr
|
|
if ( LoadUtf8((ushort)i, &cd) )
|
|
parse_const_desc((ushort)i, &cd);
|
|
break;
|
|
|
|
case CONSTANT_MethodHandle:
|
|
cd._mhr_kind = read1();
|
|
cd._mhr_index = read2();
|
|
break;
|
|
|
|
case CONSTANT_MethodType:
|
|
k = read2(); // descriptor_index
|
|
if ( k == 0 || k > curClass.maxCPindex )
|
|
goto badindex;
|
|
cd._mtd_index = k;
|
|
break;
|
|
|
|
case CONSTANT_InvokeDynamic:
|
|
{
|
|
cd._bmidx = read2(); // bootstrap_method_attr_index
|
|
k = read2(); // name_and_type_index
|
|
if ( k == 0 || k > curClass.maxCPindex )
|
|
goto badindex;
|
|
cd._name = k;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
loader_failure("Bad constant type 0x%x (%u)", cd.type, i);
|
|
} // end switch
|
|
StoreOpis(i, cd);
|
|
if ( cd.type == CONSTANT_Long || cd.type == CONSTANT_Double )
|
|
{
|
|
if ( curClass.maxCPindex == (ushort)i )
|
|
loader_failure("Premature end of constant pool");
|
|
++i;
|
|
}
|
|
} // end for
|
|
ResW_free(); // free mem - this set not needed later
|
|
|
|
msg("checking...");
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
const_desc_t cr;
|
|
ConstantNode.supval(i, &cd, sizeof(cd));
|
|
switch ( cd.type )
|
|
{
|
|
case CONSTANT_String:
|
|
if ( !LoadAnyString(lm_need_cr, cd._name) )
|
|
badref_loader_failure(i, cd._name);
|
|
continue;
|
|
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Double:
|
|
++i;
|
|
default:
|
|
continue;
|
|
|
|
case CONSTANT_NameAndType:
|
|
if ( !CheckFieldName(lm_need_cr, cd._class, &cr) )
|
|
badref_loader_failure(i, cd._class);
|
|
cd.flag |= ((cr.flag & HAS_FLDNAME) << SUB_SHIFT);
|
|
if ( !CheckAnyDscr(lm_need_cr, cd._name, &cr) )
|
|
badref_loader_failure(i, cd._name);
|
|
cd.flag |= ((cr.flag<<SUB_SHIFT) & (SUB_TYPEDSCR | SUB_CALLDSCR));
|
|
break;
|
|
|
|
case CONSTANT_Class:
|
|
if ( !CheckClass(lm_need_cr, cd._name, &cr) )
|
|
badref_loader_failure(i, cd._name);
|
|
cd.flag |= (cr.flag & (HAS_FLDNAME | HAS_TYPEDSCR | HAS_CLSNAME));
|
|
cd._dscr = cd._subnam = 0;
|
|
break;
|
|
} // end switch
|
|
StoreOpis(i, cd);
|
|
if ( (loadMode & MLD_EXTREF)
|
|
&& cd.type == CONSTANT_Class
|
|
&& (cd.flag & HAS_CLSNAME) )
|
|
{
|
|
ushort j;
|
|
|
|
for ( j = 1; j <= curClass.xtrnCnt; j++ )
|
|
{
|
|
uint32 rfc = (uint32)XtrnNode.altval(j);
|
|
if ( !CmpString(cd._name, (ushort)rfc) )
|
|
{
|
|
cd._subnam = (ushort)(rfc >> 16);
|
|
goto found;
|
|
}
|
|
}
|
|
XtrnNode.altset(j, (i << 16) | cd._name);
|
|
++curClass.xtrnCnt;
|
|
cd._subnam = (ushort)i;
|
|
found:
|
|
StoreOpis(i, cd);
|
|
}
|
|
} // end for
|
|
if ( loadMode & MLD_EXTREF )
|
|
XtrnNode.altdel(); // delete all
|
|
|
|
msg("referencing...");
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
const_desc_t cr;
|
|
uint32 sav;
|
|
|
|
ConstantNode.supval(i, &cd, sizeof(cd));
|
|
switch ( cd.type )
|
|
{
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Double:
|
|
++i;
|
|
default:
|
|
continue;
|
|
|
|
case CONSTANT_Class:
|
|
continue;
|
|
|
|
case CONSTANT_InterfaceMethodref:
|
|
case CONSTANT_Fieldref:
|
|
case CONSTANT_Methodref:
|
|
if ( !LoadOpis(lm_need_cr, cd._class, CONSTANT_Class, &cr) )
|
|
badref_loader_failure(i, cd._class);
|
|
//\\VALID NULL ??? go twos if any... (reorder cur ind to null)
|
|
cd.flag |= (cr.flag & HAS_CLSNAME);
|
|
k = cd._name;
|
|
sav = errload;
|
|
cd.ref_ip = cr._subnam;
|
|
CheckClassName(lm_need_cr, cd._name = cr._name, NULL); //lint !e530 likely using an uninitialized value
|
|
if ( !LoadOpis(lm_need_cr, k, CONSTANT_NameAndType, &cr) )
|
|
{
|
|
badref_loader_failure(i, k);
|
|
}
|
|
cd._dscr = cr._name;
|
|
cd._subnam = cr._class;
|
|
cd.flag |=(cr.flag & (SUB_FLDNAME | SUB_TYPEDSCR | SUB_CALLDSCR));
|
|
if ( cd.type != CONSTANT_Fieldref )
|
|
CheckCallDscr(lm_need_cr, cd._dscr, NULL);
|
|
else
|
|
CheckFieldDscr(lm_need_cr, cd._dscr, NULL);
|
|
if ( !LoadFieldName(lm_need_cr, cd._subnam) )
|
|
--sav;
|
|
if ( (loadMode & MLD_EXTREF) && errload == sav )
|
|
{
|
|
XtrnNode.altset(++ui, i, '0');
|
|
++curClass.xtrnCnt;
|
|
}
|
|
else
|
|
{
|
|
cd.ref_ip = 0;
|
|
}
|
|
break;
|
|
|
|
case CONSTANT_MethodHandle:
|
|
if ( !LoadOpis(lm_need_cr, cd._mhr_index, 0, &cr) )
|
|
badref_loader_failure(i, cd._mhr_index);
|
|
break;
|
|
|
|
case CONSTANT_MethodType:
|
|
if ( !LoadOpis(lm_need_cr, cd._mtd_index, CONSTANT_Utf8, &cr) )
|
|
badref_loader_failure(i, cd._mtd_index);
|
|
break;
|
|
|
|
case CONSTANT_InvokeDynamic:
|
|
// We can resolve the name_and_type_index, right away but the
|
|
// bootstrap_method_attr_index, will have to wait after the
|
|
// BootstrapMethods array has been loaded (in load_attributes())
|
|
if ( !LoadOpis(lm_need_cr, cd._name, CONSTANT_NameAndType, &cr) )
|
|
badref_loader_failure(i, cd._mtd_index);
|
|
break;
|
|
} // end switch
|
|
StoreOpis(i, cd);
|
|
} // end for
|
|
|
|
msg("complete\n");
|
|
|
|
// { // debug
|
|
// intvec_t tmp;
|
|
// FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
// tmp.push_back(i);
|
|
// dump_floating_constants(
|
|
// "NO PROBLEM",
|
|
// "NOTHING",
|
|
// tmp);
|
|
// }
|
|
|
|
return ui;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void java_t::setPoolReference(void)
|
|
{
|
|
char str[MAXNAMELEN];
|
|
const_desc_t co;
|
|
uint ic, ii, ui = 0;
|
|
|
|
msg("Sorting external references...");
|
|
for ( uint i = 1; (ushort)i <= curClass.xtrnCnt; i++ )
|
|
{
|
|
uint j = (uint)XtrnNode.altval(i, '0');
|
|
if ( j == 0 )
|
|
continue;
|
|
show_addr(curClass.xtrnCnt - (ushort)i);
|
|
ConstantNode.supval(j, &co, sizeof(co));
|
|
if ( co._class == curClass.This.Dscr )
|
|
{
|
|
co.ref_ip = 0;
|
|
StoreOpis(j, co);
|
|
continue;
|
|
}
|
|
const_desc_t cr;
|
|
ConstantNode.supval(ic = co.ref_ip, &cr, sizeof(cr));
|
|
xtrnSet(ic, &cr, ++ui, str, sizeof(str), true);
|
|
xtrnSet(j, &co, ++ui, str, sizeof(str), false);
|
|
deltry(ii = i + 1, ic, ui, co);
|
|
for ( ; (ushort)ii <= curClass.xtrnCnt; ii++ )
|
|
{
|
|
j = (uint)XtrnNode.altval(ii, '0');
|
|
if ( j == 0 )
|
|
continue;
|
|
ConstantNode.supval(j, &cr, sizeof(cr));
|
|
if ( cr.ref_ip != (ushort)ic )
|
|
continue;
|
|
xtrnSet(j, &cr, ++ui, str, sizeof(str), false);
|
|
XtrnNode.altdel(ii, '0');
|
|
deltry(ii + 1, ic, ui, co);
|
|
}
|
|
}
|
|
XtrnNode.altdel_all('0');
|
|
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
ConstantNode.supval(i, &co, sizeof(co));
|
|
switch ( co.type )
|
|
{
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Double:
|
|
++i;
|
|
default:
|
|
break;
|
|
case CONSTANT_Class:
|
|
if ( co._subnam == (ushort)i
|
|
&& (ushort)i != curClass.This.Dscr
|
|
&& !co.ref_ip )
|
|
{
|
|
xtrnSet(i, &co, ++ui, str, sizeof(str), true);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
ConstantNode.supval(i, &co, sizeof(co));
|
|
switch ( co.type )
|
|
{
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Double:
|
|
++i;
|
|
default:
|
|
continue;
|
|
case CONSTANT_Class:
|
|
break;
|
|
}
|
|
if ( co._subnam && co._subnam != (ushort)i )
|
|
{
|
|
const_desc_t tmp;
|
|
ConstantNode.supval(co._subnam, &tmp, sizeof(tmp));
|
|
co.ref_ip = tmp.ref_ip;
|
|
StoreOpis(i, co);
|
|
}
|
|
}
|
|
|
|
curClass.xtrnCnt = (ushort)ui;
|
|
if ( curClass.xtrnCnt != 0 )
|
|
{
|
|
set_segm_end(curClass.xtrnEA, curClass.xtrnEA + curClass.xtrnCnt + 1, SEGMOD_KILL);
|
|
create_byte(curClass.xtrnEA, 1);
|
|
}
|
|
else
|
|
{
|
|
XtrnNode.kill();
|
|
curClass.xtrnNode = 0;
|
|
del_segm(curClass.xtrnEA, SEGMOD_KILL);
|
|
curClass.xtrnEA = 0;
|
|
}
|
|
msg("OK\n");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void java_t::CheckPoolReference(bool insns_created)
|
|
{
|
|
char str[MAX_ATTR_NMSZ];
|
|
const_desc_t co;
|
|
intvec_t k1s;
|
|
intvec_t k2s;
|
|
intvec_t k3s;
|
|
uint mask = attr_CHECK_MASK;
|
|
|
|
msg("Checking references, %s creating instructions...\n", insns_created ? "after" : "before");
|
|
FOR_EACH_CONSTANT_POOL_INDEX(i)
|
|
{
|
|
ConstantNode.supval(i, &co, sizeof(co));
|
|
if ( co.type == CONSTANT_Long || co.type == CONSTANT_Double )
|
|
++i;
|
|
if ( co.is_referenced() )
|
|
continue;
|
|
switch ( co.type )
|
|
{
|
|
case CONSTANT_Utf8:
|
|
if ( !insns_created )
|
|
{
|
|
uchar j;
|
|
|
|
k2s.push_back(i);
|
|
CASSERT((1 << (attr_LocalVariables+1)) - 1 == attr_CHECK_MASK);
|
|
if ( (co.flag & HAS_FLDNAME)
|
|
&& mask
|
|
&& (j = attribute_type_from_str((ushort)i, attr_parent_kind_CHECK, str)) <= attr_LocalVariables
|
|
&& (mask & (1 << j)) )
|
|
{
|
|
mask ^= (1 << j);
|
|
}
|
|
else if ( co._Ssize )
|
|
{
|
|
k3s.push_back(i); // unnotify empty
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CONSTANT_NameAndType:
|
|
if ( !insns_created )
|
|
k1s.push_back(i);
|
|
break;
|
|
|
|
case CONSTANT_Class:
|
|
if ( insns_created )
|
|
{
|
|
k2s.push_back(i);
|
|
if ( !(co.flag & HAS_CLSNAME) )
|
|
k3s.push_back(i);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if ( insns_created )
|
|
k1s.push_back(i);
|
|
break;
|
|
} // switch
|
|
} // for
|
|
|
|
if ( !k1s.empty() )
|
|
{
|
|
dump_floating_constants(
|
|
"unused",
|
|
insns_created ? "(any except Class/Type/String)" : "NameAndType",
|
|
k1s);
|
|
}
|
|
if ( !k2s.empty() )
|
|
{
|
|
if ( !k3s.empty() )
|
|
{
|
|
dump_floating_constants(
|
|
insns_created ? "unnamed" : "unreferenced",
|
|
insns_created ? "Class" : "Utf8",
|
|
k3s);
|
|
for ( size_t i = 0, n = k3s.size(); i < n; ++i )
|
|
k2s.del(k3s[i]);
|
|
}
|
|
if ( !k2s.empty() )
|
|
dump_floating_constants("unused", insns_created ? "Class" : "Utf8", k2s);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void java_t::ValidateStoreLocVar(ushort slot, LocVar & lv)
|
|
{
|
|
netnode temp;
|
|
uint32 cnt, id;
|
|
bool dble;
|
|
LocVar vals[(qmin(MAXSTR, MAXSPECSIZE)/sizeof(LocVar))];
|
|
const char *txt = "Invalid declaration";
|
|
|
|
lv.ScopeTop = (ushort)(id = (uint32)lv.ScopeBeg + lv.ScopeTop);
|
|
|
|
if ( slot >= curSeg.DataSize || id > curSeg.CodeSize )
|
|
goto BADDECL;
|
|
|
|
dble = false;
|
|
if ( curSeg.varNode )
|
|
{
|
|
temp = curSeg.varNode;
|
|
cnt = (uint32)temp.altval(slot);
|
|
if ( cnt != 0 )
|
|
{
|
|
if ( (int32)cnt < 0 )
|
|
{
|
|
cnt = -(int32)cnt;
|
|
dble = true;
|
|
}
|
|
if ( (cnt % sizeof(LocVar))
|
|
|| cnt >= sizeof(vals)
|
|
|| temp.supval(slot, vals, cnt+1) != cnt )
|
|
{
|
|
goto interr;
|
|
}
|
|
cnt /= sizeof(LocVar);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
temp.create();
|
|
curSeg.varNode = temp;
|
|
cnt = 0;
|
|
}
|
|
|
|
if ( !lv.utsign ) // base declaration
|
|
{
|
|
const_desc_t opis;
|
|
|
|
CASSERT(offsetof(LocVar, utsign)+sizeof(lv.utsign) == sizeof(LocVar));
|
|
//lint -esym(645, vals) Symbol may not have been initialized
|
|
for ( id = 0; id < cnt; id++ ) // skip full duplication
|
|
if ( memcmp(&lv, &vals[id], offsetof(LocVar, utsign)) == 0 )
|
|
return;
|
|
|
|
if ( !LoadFieldName(lm_normal, lv.var.Name) )
|
|
goto BADDECL;
|
|
if ( !LoadFieldDscr(lm_normal, lv.var.Dscr, &opis) )
|
|
goto BADDECL;
|
|
|
|
CASSERT(offsetof(const_desc_t, _Sflags) - offsetof(const_desc_t, _Ssize) == 2);
|
|
if ( !dble && opis._Sopstr == (1 | (_OP_UTF8_ << 16)) )
|
|
{
|
|
uchar tmp[sizeof(ushort)+1];
|
|
if ( ConstantNode.supval((uint32)lv.var.Dscr << 16, tmp, sizeof(tmp),
|
|
BLOB_TAG) != sizeof(ushort) )
|
|
goto interr;
|
|
switch ( tmp[0] )
|
|
{
|
|
case j_double:
|
|
case j_long:
|
|
dble = true;
|
|
if ( slot+1 == curSeg.DataSize )
|
|
goto BADDECL;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
txt = "Too many variants";
|
|
if ( cnt == qnumber(vals)-1 )
|
|
goto BADDECL;
|
|
|
|
if ( !lv.ScopeBeg )
|
|
curSeg.id.extflg |= XFL_M_LABSTART; // special label at entry
|
|
if ( lv.ScopeTop == curSeg.CodeSize )
|
|
curSeg.id.extflg |= XFL_M_LABEND; // special label at end
|
|
|
|
{
|
|
ea_t dea = curSeg.DataBase + slot;
|
|
add_dref(dea, curSeg.start_ea + lv.ScopeBeg, dr_I);
|
|
add_dref(dea, curSeg.start_ea + lv.ScopeTop, dr_I);
|
|
}
|
|
xtrnRef_dscr(curSeg.start_ea + lv.ScopeBeg, &opis);
|
|
if ( !cnt )
|
|
set_lv_name(lv.var.Name, curSeg.DataBase + slot,
|
|
(loadMode & MLD_LOCVAR) ? 3 : 0); // if not rename_on_load ONLY mark
|
|
vals[cnt++] = lv;
|
|
}
|
|
else
|
|
{ // signature declaration
|
|
CASSERT(offsetof(LocVar, var.Dscr)+sizeof(lv.var.Dscr)+sizeof(lv.utsign) == sizeof(LocVar));
|
|
for ( id = 0; id < cnt; id++ )
|
|
if ( memcmp(&lv, &vals[id], offsetof(LocVar, var.Dscr)) == 0 )
|
|
{
|
|
if ( !vals[id].utsign )
|
|
{
|
|
if ( !CheckSignature(lv.utsign, attr_parent_kind_code) )
|
|
goto BADDECL;
|
|
vals[id].utsign = lv.utsign;
|
|
goto store;
|
|
}
|
|
if ( vals[id].utsign == lv.utsign )
|
|
return;
|
|
txt = "Different signature";
|
|
goto BADDECL;
|
|
}
|
|
txt = "Signature without type";
|
|
goto BADDECL;
|
|
}
|
|
store:
|
|
cnt *= sizeof(LocVar);
|
|
temp.supset(slot, vals, cnt);
|
|
if ( !lv.utsign )
|
|
{
|
|
if ( dble )
|
|
cnt = -(int32)cnt;
|
|
temp.altset(slot, cnt);
|
|
}
|
|
return;
|
|
|
|
BADDECL:
|
|
load_msg("%s LocVar#%u Method#%u (name#%u dsc#%u sgn#%u scope:%u-%u)\n",
|
|
txt, slot, curSeg.id.Number,
|
|
lv.var.Name, lv.var.Dscr, lv.utsign, lv.ScopeBeg, lv.ScopeTop);
|
|
return;
|
|
|
|
interr:
|
|
INTERNAL("StoreLocVar");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
inline void java_t::BadRefFile(const char *to, ushort id)
|
|
{
|
|
BadRef(BADADDR, to, id, attr_parent_kind_class_file);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
uchar *java_t::annotation(uchar *p, uint32 *psize)
|
|
{
|
|
if ( *psize < 2 )
|
|
{
|
|
bad:
|
|
return NULL;
|
|
}
|
|
*psize -= 2;
|
|
uint pairs= read2();
|
|
*(ushort *)p = (ushort)pairs;
|
|
p += sizeof(ushort);
|
|
if ( pairs )
|
|
{
|
|
do
|
|
{
|
|
if ( *psize < 2 )
|
|
goto bad;
|
|
*psize -= 2;
|
|
ushort id = read2();
|
|
if ( !LoadFieldName(lm_normal, id) )
|
|
goto bad;
|
|
*(ushort *)p = id;
|
|
p = annot_elm(p+sizeof(ushort), psize);
|
|
if ( p == NULL )
|
|
goto bad;
|
|
}
|
|
while ( --pairs );
|
|
}
|
|
return p;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
uchar *java_t::annot_elm(uchar *ptr, uint32 *psize, uchar is_array)
|
|
{
|
|
if ( *psize < 1+2 )
|
|
{
|
|
bad:
|
|
return NULL;
|
|
}
|
|
*psize -= 1+2;
|
|
union
|
|
{
|
|
uchar *p1;
|
|
ushort *p2;
|
|
};
|
|
p1 = ptr;
|
|
uchar tag = read1();
|
|
ushort val = read2();
|
|
*p1++ = tag;
|
|
*p2++ = val;
|
|
switch ( tag )
|
|
{
|
|
case j_annotation:
|
|
if ( isSingleClass(val)
|
|
&& (p1 = annotation(p1, psize)) != NULL )
|
|
{
|
|
goto done;
|
|
}
|
|
default:
|
|
goto bad;
|
|
|
|
case j_array:
|
|
if ( val && !is_array ) // multidimensional array is not valid (javac )
|
|
{
|
|
uchar *ps = p1;
|
|
tag = 0;
|
|
do
|
|
{
|
|
p1 = annot_elm(p1, psize, 1);
|
|
if ( p1 == NULL )
|
|
goto bad;
|
|
if ( !tag )
|
|
{
|
|
if ( val == 1 )
|
|
break;
|
|
tag = *ps;
|
|
ps = p1;
|
|
}
|
|
else if ( tag != (uchar)-1 )
|
|
{
|
|
if ( tag != *ps )
|
|
goto bad;
|
|
tag = (uchar)-1;
|
|
}
|
|
}
|
|
while ( --val );
|
|
goto done;
|
|
}
|
|
goto bad;
|
|
|
|
case j_enumconst:
|
|
if ( LoadFieldDscr(lm_normal, val, NULL) )
|
|
{
|
|
if ( *psize < 2 )
|
|
goto bad;
|
|
*psize -= 2;
|
|
*p2++ = val = read2();
|
|
if ( LoadFieldName(lm_normal, val) )
|
|
goto done;
|
|
}
|
|
goto bad;
|
|
|
|
case j_class_ret:
|
|
if ( isSingleClass(val) )
|
|
goto done;
|
|
goto bad; //### in 'classfile.pdf' j_void_ret also remebemered?
|
|
|
|
case j_string:
|
|
tag = CONSTANT_Utf8;
|
|
break;
|
|
case j_float:
|
|
tag = CONSTANT_Float;
|
|
break;
|
|
case j_long:
|
|
tag = CONSTANT_Long;
|
|
break;
|
|
case j_double:
|
|
tag = CONSTANT_Double;
|
|
break;
|
|
case j_int:
|
|
case j_byte:
|
|
case j_char:
|
|
case j_short:
|
|
case j_bool:
|
|
tag = CONSTANT_Integer;
|
|
break;
|
|
}
|
|
if ( !LoadOpis(lm_normal, val, tag, NULL) )
|
|
goto bad;
|
|
done:
|
|
return p1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
bool java_t::sm_chkargs(uchar **pptr, uint32 *pDopSize, ushort cnt)
|
|
{
|
|
union
|
|
{
|
|
uchar *p1;
|
|
ushort *p2;
|
|
};
|
|
p1 = *pptr;
|
|
uint32 dopsize = *pDopSize;
|
|
bool result = false;
|
|
|
|
do
|
|
{
|
|
if ( !dopsize )
|
|
goto declerr_w;
|
|
--dopsize;
|
|
uchar tag = read1();
|
|
if ( tag > ITEM_Uninitialized )
|
|
goto declerr_w;
|
|
*p1++ = tag;
|
|
CASSERT(ITEM_Object+1 == ITEM_Uninitialized);
|
|
if ( tag >= ITEM_Object )
|
|
{
|
|
if ( dopsize < 2 )
|
|
goto declerr_w;
|
|
dopsize -= 2;
|
|
ushort var = read2();
|
|
if ( tag == ITEM_Object )
|
|
{
|
|
const_desc_t opis;
|
|
if ( var == curClass.This.Dscr )
|
|
{
|
|
var = curClass.This.Name;
|
|
p1[-1] = ITEM_CURCLASS;
|
|
}
|
|
else if ( LoadNamedClass(lm_normal, var, &opis) )
|
|
{
|
|
var = opis._name;
|
|
}
|
|
else
|
|
{
|
|
p1[-1] = ITEM_BADOBJECT;
|
|
}
|
|
}
|
|
else
|
|
{ // Uninitialized (offset to new instruction)
|
|
if ( !var )
|
|
curSeg.id.extflg |= XFL_M_LABSTART; // PARANOYA
|
|
else if ( var >= curSeg.CodeSize )
|
|
goto declerr_w;
|
|
}
|
|
*p2++ = var;
|
|
}
|
|
}
|
|
while ( --cnt );
|
|
result = true;
|
|
*pptr = p1;
|
|
declerr_w:
|
|
*pDopSize = dopsize;
|
|
return result;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
int java_t::sm_load(ushort declcnt, uint32 *pDopSize)
|
|
{
|
|
union
|
|
{
|
|
uchar *p1;
|
|
ushort *p2;
|
|
};
|
|
sm_info_t smr;
|
|
uint32 dopsize = *pDopSize;
|
|
netnode temp(curSeg.smNode);
|
|
int result = 0;
|
|
uint prevoff = (uint)-1;
|
|
|
|
p1 = sm_realloc(dopsize);
|
|
dopsize -= 2; // skip READED counter
|
|
*p2++ = declcnt; // counter
|
|
smr.noff = (uint32)(p1 - smBuf);
|
|
smr.fcnt = 0;
|
|
do
|
|
{
|
|
ea_t refea;
|
|
uint nxcnt;
|
|
uchar rectype = SMT_FULL_FRAME;
|
|
if ( SMF_mode ) // >=JDK6
|
|
{
|
|
if ( !dopsize )
|
|
goto declerr_w;
|
|
--dopsize;
|
|
rectype = read1();
|
|
*p1++ = rectype;
|
|
}
|
|
{
|
|
uint off;
|
|
if ( rectype < SMT_SAME_FRM_S1 )
|
|
{
|
|
if ( rectype > SMT_SAME_FRM_S1_max )
|
|
goto declerr_w; // reserved
|
|
off = rectype;
|
|
if ( rectype >= SMT_SAME_FRM_S1_min )
|
|
off -= SMT_SAME_FRM_S1_min;
|
|
}
|
|
else
|
|
{
|
|
if ( dopsize < 2 )
|
|
goto declerr_w;
|
|
dopsize -= 2;
|
|
off = read2();
|
|
*p2++ = (ushort)off;
|
|
}
|
|
if ( SMF_mode )
|
|
off += (prevoff + 1); // >=JDK6
|
|
if ( (uint32)off >= curSeg.CodeSize )
|
|
goto declerr_w;
|
|
prevoff = off;
|
|
refea = curSeg.start_ea + off;
|
|
}
|
|
if ( temp.supval(refea, NULL, 0) != -1 ) // for CLDC only
|
|
{
|
|
--result;
|
|
goto declerr_w;
|
|
}
|
|
nxcnt = smr.fcnt;
|
|
if ( rectype == SMT_FULL_FRAME )
|
|
{
|
|
for ( int pass = 0; pass < 2; pass++ )
|
|
{
|
|
ushort cnt;
|
|
|
|
if ( dopsize < 2 )
|
|
goto declerr_w;
|
|
dopsize -= 2;
|
|
*p2++ = cnt = read2(); // number of locals / number of stacks
|
|
if ( !pass )
|
|
nxcnt = cnt;
|
|
if ( cnt && !sm_chkargs(&p1, &dopsize, cnt) )
|
|
goto declerr_w;
|
|
}
|
|
}
|
|
else if ( rectype > SMT_SAME_FRM_S0 )
|
|
{
|
|
rectype -= SMT_SAME_FRM_S0;
|
|
if ( !sm_chkargs(&p1, &dopsize, rectype) )
|
|
goto declerr_w;
|
|
nxcnt += rectype;
|
|
}
|
|
else if ( rectype >= SMT_SAME_FRM_S1 )
|
|
{
|
|
rectype = (uchar)SMT_SAME_FRM_S0 - rectype;
|
|
nxcnt -= rectype;
|
|
if ( int(nxcnt) < 0 )
|
|
goto declerr_w;
|
|
if ( rectype == 4 /*i.e., was: SMT_SAME_FRM_S1 */ )
|
|
{
|
|
// same_locals_1_stack_item_frame_extended
|
|
// http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4
|
|
if ( !sm_chkargs(&p1, &dopsize, 1) )
|
|
goto declerr_w;
|
|
}
|
|
}
|
|
else if ( rectype >= SMT_SAME_FRM_S1_min )
|
|
{
|
|
if ( !sm_chkargs(&p1, &dopsize, 1) )
|
|
goto declerr_w;
|
|
}
|
|
smr.eoff = (uint32)(p1 - smBuf);
|
|
temp.supset(refea, &smr, sizeof(smr));
|
|
smr.noff = smr.eoff;
|
|
smr.fcnt = nxcnt;
|
|
}
|
|
while ( --declcnt );
|
|
//
|
|
temp.altset(-1, smr.noff);
|
|
temp.setblob(smBuf, smr.noff, 0, BLOB_TAG);
|
|
++result;
|
|
declerr_w:
|
|
*pDopSize = dopsize;
|
|
return result;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
NORETURN void java_t::loader_failure_bad_attr_decl_size(attr_parent_kind_t apk) const
|
|
{
|
|
char diastr[128];
|
|
loader_failure("Illegal declaration size%s", mk_diag(apk, diastr));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
void java_t::load_attributes(attr_parent_kind_t apk)
|
|
{
|
|
char atrs[MAX_ATTR_NMSZ+2], diastr[128];
|
|
// ushort k;
|
|
uint i, j, r;
|
|
netnode temp;
|
|
uval_t *pann;
|
|
uchar eflg, lvtb = 0, lntb = 0, fatal = (loadMode & MLD_FORCE) == 0;
|
|
const_desc_t opis;
|
|
opis._name = 0; // for vc
|
|
|
|
j = read2();
|
|
for ( i = 0; i < j; i++ )
|
|
{
|
|
if ( savesize >= 0 && (savesize -= 6) < 0 )
|
|
loader_failure_bad_attr_decl_size(apk);
|
|
const ushort attribute_name_index = read2();
|
|
uint attribute_length = read4();
|
|
if ( savesize >= 0 )
|
|
{
|
|
if ( (uint)savesize < attribute_length )
|
|
loader_failure_bad_attr_decl_size(apk);
|
|
savesize -= attribute_length;
|
|
}
|
|
eflg = 0; // flag(additional attributes/locvars/annotation/stackmap)
|
|
atrs[0] = ' '; // for additinal attibutes (mark SourceDebugExtension)
|
|
switch ( attribute_type_from_str(attribute_name_index, apk, &atrs[1]) )
|
|
{
|
|
case attr_SourceDebugExtension:
|
|
if ( sde )
|
|
goto duplerr_w;
|
|
++sde;
|
|
if ( attribute_length < 0x10000 )
|
|
{
|
|
if ( attribute_length == 0 )
|
|
{
|
|
deb(IDA_DEBUG_LDR,
|
|
"Ignore zero length SourceDebugExtension attribute\n");
|
|
break;
|
|
}
|
|
qoff64_t pos = qftell(myFile), ssz = FileSize;
|
|
if ( LoadUtf8((ushort)-1, (const_desc_t*)(size_t)attribute_length) )
|
|
{
|
|
curClass.extflg |= XFL_C_DEBEXT;
|
|
break;
|
|
}
|
|
qfseek(myFile, pos, SEEK_SET);
|
|
FileSize = ssz;
|
|
}
|
|
if ( ask_yn(ASKBTN_YES,
|
|
"HIDECANCEL\n"
|
|
"SourceDebugExtension attribute have a non standard encoding\n"
|
|
"or large size and cannot be represented in assembler.\n\n"
|
|
"\3Do you want to store it in external file?") > ASKBTN_NO )
|
|
goto attr2file;
|
|
goto skipAttr;
|
|
|
|
case attr_BootstrapMethods:
|
|
{
|
|
if ( bootstrap_methods_get_node(/*assert=*/ false) != BADNODE )
|
|
loader_failure("Duplicate 'BootstrapMethods' attribute definition");
|
|
bootstrap_methods_get_node(/*assert=*/ true, /*can_create=*/ true);
|
|
if ( attribute_length < 2 )
|
|
goto declerr_w;
|
|
const ushort num_bootstrap_methods = read2();
|
|
bootstrap_methods_set_count(num_bootstrap_methods);
|
|
attribute_length -= 2;
|
|
const qoff64_t end = qftell(myFile) + attribute_length;
|
|
for ( ushort bmidx = 0; bmidx < num_bootstrap_methods; ++bmidx )
|
|
{
|
|
if ( qftell(myFile) >= end )
|
|
goto declerr_w;
|
|
const ushort bootstrap_method_ref = read2();
|
|
|
|
if ( qftell(myFile) >= end )
|
|
goto declerr_w;
|
|
const ushort num_bootstrap_arguments = read2();
|
|
|
|
bootstrap_method_def_t mdef;
|
|
mdef.method_ref = bootstrap_method_ref;
|
|
for ( ushort baidx = 0; baidx < num_bootstrap_arguments; ++baidx )
|
|
{
|
|
if ( qftell(myFile) >= end )
|
|
goto declerr_w;
|
|
const ushort bootstrap_argument = read2();
|
|
mdef.args.push_back(bootstrap_argument);
|
|
}
|
|
|
|
bootstrap_methods_set_method(mdef, bmidx);
|
|
}
|
|
|
|
// Now that all bootstrap methods are loaded, try and resolve
|
|
// the constant pool's references to it
|
|
FOR_EACH_CONSTANT_POOL_INDEX(cpidx)
|
|
{
|
|
const_desc_t cd;
|
|
cd.type = 0;
|
|
ConstantNode.supval(cpidx, &cd, sizeof(cd));
|
|
if ( cd.type == CONSTANT_InvokeDynamic )
|
|
{
|
|
bootstrap_method_def_t mdef;
|
|
if ( bootstrap_methods_get_method(&mdef, cd._bmidx) )
|
|
LoadOpis(lm_normal, mdef.method_ref, CONSTANT_MethodHandle, NULL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case attr_UNKNOWN:
|
|
atrs[0] = uchar(apk);
|
|
if ( loadMode & MLD_EXTATR )
|
|
{
|
|
attr2file:
|
|
const char *p = CopyAttrToFile(atrs, attribute_length, attribute_name_index);
|
|
if ( p == NULL )
|
|
break;
|
|
loader_failure("%s %sattribute '%s'%s",
|
|
p,
|
|
atrs[0] == ' ' ? "" : "additional ",
|
|
&atrs[1],
|
|
mk_diag(apk, diastr));
|
|
}
|
|
if ( idpflags & IDM_REQUNK )
|
|
{
|
|
idpflags &= ~IDM_REQUNK;
|
|
if ( ask_yn(ASKBTN_YES,
|
|
"HIDECANCEL\n"
|
|
"File contains unknown attribute(s).\n"
|
|
"Do you want store it in external files?\n\n") > ASKBTN_NO )
|
|
{
|
|
loadMode |= MLD_EXTATR;
|
|
goto attr2file;
|
|
}
|
|
}
|
|
eflg = 1;
|
|
goto unkAttr;
|
|
case attr_NONAME:
|
|
qsnprintf(atrs, sizeof(atrs), "(with index %u)", attribute_name_index);
|
|
goto notify;
|
|
case attr_TRUNCATED:
|
|
qstrncat(atrs, "...", sizeof(atrs));
|
|
unkAttr:
|
|
atrs[0] = '"';
|
|
qstrncat(atrs, "\"", sizeof(atrs));
|
|
notify:
|
|
#ifdef _MSC_VER
|
|
#pragma warning(push)
|
|
#pragma warning(disable : 4191)
|
|
#endif
|
|
if ( eflg && !(idpflags & IDM_WARNUNK) )
|
|
msg(
|
|
"Ignore%s %s attribute (size %u)%s\n",
|
|
(uchar)atrs[0] == ' ' ? "" : " unknown",
|
|
atrs, attribute_length, mk_diag(apk, diastr));
|
|
else
|
|
load_msg(
|
|
"Ignore%s %s attribute (size %u)%s\n",
|
|
(uchar)atrs[0] == ' ' ? "" : " unknown",
|
|
atrs, attribute_length, mk_diag(apk, diastr));
|
|
#ifdef _MSC_VER
|
|
#pragma warning(pop)
|
|
#endif
|
|
skipAttr:
|
|
if ( attribute_length )
|
|
{
|
|
real_skipAttr:
|
|
skipData(attribute_length);
|
|
}
|
|
break;
|
|
|
|
case attr_ConstantValue:
|
|
{
|
|
ushort constantvalue_index;
|
|
if ( attribute_length != 2 || !LoadOpis(lm_normal, constantvalue_index = read2(), 0, &opis) )
|
|
{
|
|
declerr:
|
|
++fatal;
|
|
declerr_w:
|
|
load_msg("Illegal declaration of %s%s\n",
|
|
&atrs[1], mk_diag(apk, diastr));
|
|
goto skipAttr;
|
|
}
|
|
|
|
temp = curField.valNode;
|
|
if ( !temp )
|
|
{
|
|
temp.create();
|
|
curField.valNode = temp;
|
|
r = 0;
|
|
}
|
|
else
|
|
{
|
|
r = (uint)temp.altval(0);
|
|
}
|
|
switch ( opis.type )
|
|
{
|
|
case CONSTANT_Integer:
|
|
case CONSTANT_Long:
|
|
case CONSTANT_Float:
|
|
case CONSTANT_Double:
|
|
case CONSTANT_String:
|
|
temp.supset(++r, &opis, sizeof(opis));
|
|
break;
|
|
default:
|
|
BadRef(curClass.start_ea + curField.id.Number, "value", constantvalue_index, apk);
|
|
temp.altset(++r, (((uint32) constantvalue_index) << 16) | 0xFFFF);
|
|
break;
|
|
}
|
|
temp.altset(0, r);
|
|
}
|
|
break;
|
|
|
|
case attr_Code:
|
|
if ( curSeg.CodeSize )
|
|
{
|
|
// duplerr:
|
|
++fatal;
|
|
duplerr_w:
|
|
if ( fatal )
|
|
loader_failure("Duplicate %s attribute declaration%s", &atrs[1], mk_diag(apk, diastr));
|
|
else
|
|
load_msg("Duplicate %s attribute declaration%s", &atrs[1], mk_diag(apk, diastr));
|
|
goto skipAttr;
|
|
}
|
|
r = curClass.JDKsubver ? 12 : 8;
|
|
if ( attribute_length < r )
|
|
goto declerr;
|
|
attribute_length -= r;
|
|
r -= 8;
|
|
curSeg.stacks = r ? read2() : read1();
|
|
curSeg.DataSize = r ? read2() : read1(); // max_locals
|
|
curSeg.CodeSize = r ? read4() : read2();
|
|
if ( curSeg.CodeSize != 0 )
|
|
{
|
|
if ( attribute_length < curSeg.CodeSize )
|
|
goto declerr;
|
|
attribute_length -= curSeg.CodeSize;
|
|
if ( FileSize < curSeg.CodeSize )
|
|
errtrunc();
|
|
FileSize -= curSeg.CodeSize;
|
|
{
|
|
segment_t *S = _add_seg(-1); // expand size
|
|
if ( curSeg.DataSize )
|
|
set_default_sreg_value(S, rVds, _add_seg(2)->sel); // create data segment
|
|
}
|
|
}
|
|
|
|
{
|
|
const ushort exception_table_length = read2();
|
|
if ( exception_table_length != 0 ) // except. table
|
|
{
|
|
if ( !curSeg.CodeSize )
|
|
goto declerr;
|
|
|
|
r = ((uint32) exception_table_length) * (2*4);
|
|
if ( attribute_length < r )
|
|
goto declerr;
|
|
attribute_length -= r;
|
|
temp.create();
|
|
curSeg.excNode = temp;
|
|
temp.altset(0, exception_table_length);
|
|
ea_t ea = curSeg.start_ea + curSeg.CodeSize;
|
|
r = 1;
|
|
uchar err = 0;
|
|
do
|
|
{
|
|
Exception exc;
|
|
|
|
exc.start_pc = read2();
|
|
exc.end_pc = read2();
|
|
exc.handler_pc = read2();
|
|
|
|
exc.filter.Ref &= 0; // for 'finally'
|
|
exc.filter.Dscr = read2();
|
|
if ( exc.filter.Dscr != 0 )
|
|
{
|
|
if ( !LoadNamedClass(lm_normal, exc.filter.Dscr, &opis) )
|
|
{
|
|
BadRef(ea, ".catch", exc.filter.Dscr, apk);
|
|
}
|
|
else
|
|
{
|
|
exc.filter.Name = opis._name;
|
|
xtrnRef(ea, opis);
|
|
}
|
|
}
|
|
temp.supset(r++, &exc, sizeof(exc));
|
|
err |= set_exception_xref(&curSeg, exc, ea); // as procedure for base converter
|
|
}
|
|
while ( (ushort)r <= exception_table_length );
|
|
if ( err )
|
|
{
|
|
remember_problem(PR_ILLADDR, ea);
|
|
load_msg("Invalid address(es) in .catch%s\n",
|
|
mk_diag(apk, diastr));
|
|
}
|
|
}
|
|
}// exception table
|
|
|
|
savesize = attribute_length;
|
|
load_attributes(attr_parent_kind_code); // Additional attr
|
|
savesize = -1;
|
|
break;
|
|
|
|
case attr_Exceptions:
|
|
if ( curSeg.thrNode )
|
|
goto duplerr_w;
|
|
if ( attribute_length < (2+2) || (attribute_length % 2) )
|
|
goto declerr_w;
|
|
attribute_length -= 2;
|
|
if ( (attribute_length / 2) != (uint32)read2() )
|
|
goto declerr_w;
|
|
attribute_length /= 2;
|
|
temp.create();
|
|
curSeg.thrNode = temp;
|
|
r = 0;
|
|
do
|
|
{
|
|
const ushort exception_index = read2();
|
|
if ( exception_index == 0 )
|
|
{
|
|
load_msg("Ignore zero exception index%s\n", mk_diag(apk, diastr));
|
|
}
|
|
else
|
|
{
|
|
uint32 refd = (uint32)exception_index << 16;
|
|
if ( !LoadNamedClass(lm_normal, exception_index, &opis) )
|
|
{
|
|
BadRef(curSeg.start_ea, ".throws", exception_index, apk);
|
|
}
|
|
else
|
|
{
|
|
refd |= opis._name;
|
|
xtrnRef(curSeg.start_ea, opis);
|
|
}
|
|
temp.altset(++r, refd);
|
|
}
|
|
}
|
|
while ( --attribute_length );
|
|
temp.altset(0, r);
|
|
break;
|
|
|
|
case attr_LineNumberTable:
|
|
if ( lntb )
|
|
goto duplerr_w;
|
|
++lntb;
|
|
r = attribute_length - 2;
|
|
if ( attribute_length < 2 || (r % 4) )
|
|
goto declerr_w;
|
|
attribute_length -= 2;
|
|
if ( (r /= 4) != (uint32)read2() )
|
|
goto declerr_w;
|
|
if ( attribute_length == 0 )
|
|
{
|
|
// Symantec error (strip) #1
|
|
deb(IDA_DEBUG_LDR,
|
|
"Stripped declaration of LineNumber table%s\n",
|
|
mk_diag(apk, diastr));
|
|
}
|
|
while ( r-- )
|
|
{
|
|
const ushort start_pc = read2();
|
|
const ushort line_number = read2();
|
|
if ( uint32(start_pc) < curSeg.CodeSize )
|
|
set_source_linnum(curSeg.start_ea + start_pc, line_number);
|
|
else
|
|
load_msg("Illegal address (%u) of source line %u%s\n", start_pc,
|
|
line_number, mk_diag(apk, diastr));
|
|
}
|
|
break;
|
|
|
|
case attr_LocalVariableTypeTable:
|
|
if ( !(lvtb & 1) ) // ATT: my be can before LocalVariableTable?
|
|
{
|
|
if ( fatal )
|
|
loader_failure("%s before LocalVariableTable%s", atrs, mk_diag(apk, diastr));
|
|
else
|
|
load_msg("%s before LocalVariableTable%s", atrs, mk_diag(apk, diastr));
|
|
goto skipAttr;
|
|
}
|
|
++eflg; // 2 <= 1 + 1
|
|
// no break
|
|
case attr_LocalVariableTable:
|
|
++eflg; // 1
|
|
if ( lvtb & eflg )
|
|
goto duplerr_w;
|
|
lvtb |= eflg;
|
|
--eflg; // unification (0/1)
|
|
r = attribute_length - 2;
|
|
if ( attribute_length < 2 || (r % 10) )
|
|
goto declerr_w;
|
|
attribute_length -= 2;
|
|
if ( (r /= 10) != (uint32)read2() )
|
|
goto declerr_w;
|
|
while ( r-- )
|
|
{
|
|
LocVar lv;
|
|
|
|
lv.ScopeBeg = read2(); // start_pc
|
|
lv.ScopeTop = read2(); // length
|
|
lv.var.Name = read2(); // name_index
|
|
CASSERT(offsetof(LocVar,utsign) == offsetof(LocVar,var.Dscr)+sizeof(lv.var.Dscr)
|
|
&& sizeof(lv.var.Dscr)*2 == sizeof(uint32));
|
|
*(uint32 *)&lv.var.Dscr &= 0;
|
|
if ( eflg == 0 ) // LocalVariableTable/LocalVariableTypeTable
|
|
lv.var.Dscr = read2(); // descriptor_index
|
|
else
|
|
lv.utsign = read2(); // descriptor_index
|
|
const ushort index = read2(); // index
|
|
if ( !eflg ) // normal table
|
|
{
|
|
if ( index == 0 && !lv.var.Name && !lv.ScopeBeg && lv.ScopeTop <= 1 )
|
|
{
|
|
// Symantec error (strip) #2
|
|
deb(IDA_DEBUG_LDR,
|
|
"Stripped declaration of local variables%s\n",
|
|
mk_diag(apk, diastr));
|
|
continue;
|
|
}
|
|
if ( (short)lv.ScopeBeg == -1 )
|
|
{
|
|
// Microsoft VisualJ++ error (purge?)
|
|
LoadAnyString(lm_normal, lv.var.Name);
|
|
LoadAnyString(lm_normal, lv.var.Dscr);
|
|
deb(IDA_DEBUG_LDR,
|
|
"Purged declaration of LocVar#%u%s\n", index,
|
|
mk_diag(apk, diastr));
|
|
continue;
|
|
}
|
|
}
|
|
ValidateStoreLocVar(index, lv);
|
|
} // while
|
|
break;
|
|
|
|
case attr_SourceFile:
|
|
{
|
|
if ( attribute_length != 2 )
|
|
goto declerr_w;
|
|
const ushort sourcefile_index = read2();
|
|
if ( LoadAnyString(lm_normal, sourcefile_index) )
|
|
curClass.SourceName = sourcefile_index;
|
|
else
|
|
BadRef(BADADDR, "source file name", sourcefile_index, apk);
|
|
}
|
|
break;
|
|
|
|
case attr_InnerClasses:
|
|
r = attribute_length - 2;
|
|
if ( attribute_length < 2 || (r % 8) )
|
|
goto declerr_w;
|
|
attribute_length -= 2;
|
|
if ( (r /= 8) != (uint32)read2() )
|
|
goto declerr_w;
|
|
if ( !r )
|
|
{
|
|
deb(IDA_DEBUG_LDR, "Stripped declaration of InnerClasses\n");
|
|
break;
|
|
}
|
|
attribute_length = r;
|
|
{
|
|
ushort flags = 0;
|
|
while ( attribute_length-- )
|
|
{
|
|
InnerClass ic;
|
|
|
|
*(uchar *)&flags = 0;
|
|
ic.inner = read2(); // inner_class_info_index
|
|
if ( ic.inner && !LoadNamedClass(lm_normal, ic.inner, &opis) )
|
|
flags |= 0x101;
|
|
else
|
|
ic.inner = opis._name;
|
|
ic.outer = read2(); // outer_class_info_index
|
|
if ( ic.outer && !LoadNamedClass(lm_normal, ic.outer, &opis) )
|
|
flags |= 0x101;
|
|
else
|
|
ic.outer = opis._name;
|
|
ic.name = read2(); // inner_class_name_index
|
|
if ( ic.name && !LoadFieldName(lm_normal, ic.name) )
|
|
flags |= 0x101;
|
|
ic.access = read2(); // inner_class_access_flags
|
|
r = ic.access & ACC_ACCESS_MASK;
|
|
if ( r & (r-1) )
|
|
flags |= 0x101;
|
|
if ( uchar(flags) == 0 )
|
|
{
|
|
temp = curClass.innerNode;
|
|
if ( !temp )
|
|
{
|
|
temp.create();
|
|
curClass.innerNode = temp;
|
|
r = 0;
|
|
}
|
|
else
|
|
{
|
|
r = (uint)temp.altval(0);
|
|
}
|
|
temp.supset(++r, &ic, sizeof(ic));
|
|
temp.altset(0, r);
|
|
}
|
|
}
|
|
if ( flags != 0 )
|
|
load_msg("Error declaration(s) in Inner Classes\n");
|
|
}
|
|
break;
|
|
|
|
case attr_EnclosingMethod:
|
|
if ( curClass.encClass )
|
|
goto duplerr_w;
|
|
if ( attribute_length != 4 )
|
|
goto declerr_w;
|
|
curClass.encClass = LoadNamedClass(lm_normal, read2(), &opis)
|
|
? opis._name
|
|
: 0xFFFF;
|
|
{
|
|
const ushort method_index = read2();
|
|
if ( curClass.encClass == 0xFFFF )
|
|
{
|
|
bad_encl:
|
|
msg("Invalid EnclosingMethod description\n");
|
|
}
|
|
else if ( method_index != 0 )
|
|
{
|
|
if ( !LoadOpis(lm_normal, method_index, CONSTANT_NameAndType, &opis)
|
|
|| !(opis.flag & SUB_FLDNAME)
|
|
|| !LoadCallDscr(lm_normal, opis._name) )
|
|
{
|
|
goto bad_encl;
|
|
}
|
|
curClass.encMethod = method_index;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case attr_Synthetic:
|
|
if ( attribute_length != 0 )
|
|
goto declerr_w;
|
|
switch ( apk )
|
|
{
|
|
default: // attr_parent_kind_code
|
|
goto declerr_w; // paranoya
|
|
case attr_parent_kind_field:
|
|
curField.id.access |= ACC_SYNTHETIC;
|
|
break;
|
|
case attr_parent_kind_method:
|
|
curSeg.id.access |= ACC_SYNTHETIC;
|
|
break;
|
|
case attr_parent_kind_class_file:
|
|
curClass.AccessFlag |= ACC_SYNTHETIC;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case attr_Deprecated:
|
|
if ( attribute_length != 0 )
|
|
goto declerr_w;
|
|
switch ( apk )
|
|
{
|
|
default: // attr_parent_kind_code
|
|
goto declerr_w; // paranoya
|
|
case attr_parent_kind_field:
|
|
curField.id.extflg |= XFL_DEPRECATED;
|
|
break;
|
|
case attr_parent_kind_method:
|
|
curSeg.id.extflg |= XFL_DEPRECATED;
|
|
break;
|
|
case attr_parent_kind_class_file:
|
|
curClass.extflg |= XFL_DEPRECATED;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case attr_Signature:
|
|
if ( attribute_length != 2 || apk == attr_parent_kind_code )
|
|
goto declerr_w;
|
|
{
|
|
const ushort signature_index = read2();
|
|
if ( CheckSignature(signature_index, apk) )
|
|
{
|
|
switch ( apk )
|
|
{
|
|
case attr_parent_kind_field:
|
|
curField.id.utsign = signature_index;
|
|
break;
|
|
case attr_parent_kind_method:
|
|
curSeg.id.utsign = signature_index;
|
|
break;
|
|
case attr_parent_kind_class_file:
|
|
curClass.utsign = signature_index;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case attr_StackMapTable:
|
|
++eflg;
|
|
// fallthrough
|
|
case attr_StackMap:
|
|
if ( !eflg != (curClass.JDKsubver < 6) )
|
|
{
|
|
if ( fatal )
|
|
loader_failure("JDK1.%u incompatible with attribute%s%s", curClass.JDKsubver, atrs, mk_diag(apk, diastr));
|
|
else
|
|
load_msg("JDK1.%u incompatible with attribute%s%s", curClass.JDKsubver, atrs, mk_diag(apk, diastr));
|
|
goto skipAttr;
|
|
}
|
|
if ( curSeg.smNode )
|
|
goto duplerr_w;
|
|
if ( attribute_length < 2 )
|
|
goto declerr_w;
|
|
{
|
|
const ushort number_of_entries = read2();
|
|
if ( number_of_entries == 0 )
|
|
{
|
|
attribute_length -= 2;
|
|
curSeg.smNode = BADNODE;
|
|
deb(IDA_DEBUG_LDR,
|
|
"Empty%s attribute%s\n", atrs, mk_diag(apk, diastr));
|
|
curSeg.id.extflg |= XFL_M_EMPTYSM;
|
|
}
|
|
else
|
|
{
|
|
temp.create();
|
|
curSeg.smNode = temp;
|
|
r = sm_load(number_of_entries, &attribute_length);
|
|
if ( int(r) <= 0 )
|
|
{
|
|
temp.kill();
|
|
curSeg.smNode = BADNODE;
|
|
if ( !r )
|
|
goto declerr_w;
|
|
if ( fatal )
|
|
loader_failure("Inconsistent declaration of %s%s", &atrs[1], mk_diag(apk, diastr));
|
|
else
|
|
load_msg("Inconsistent declaration of %s%s", &atrs[1], mk_diag(apk, diastr));
|
|
goto skipAttr; //-V779 Unreachable code detected
|
|
}
|
|
}
|
|
}
|
|
skip_excess:
|
|
if ( attribute_length )
|
|
{
|
|
deb(IDA_DEBUG_LDR,
|
|
"Excess %u bytes in%s attribute%s\n", attribute_length, atrs,
|
|
mk_diag(apk, diastr));
|
|
goto real_skipAttr;
|
|
}
|
|
break;
|
|
|
|
case attr_AnnotationDefault:
|
|
pann = &curSeg.annNodes[2];
|
|
eflg = 4; // as flag
|
|
goto do_annot1;
|
|
case attr_RuntimeInvisibleParameterAnnotations:
|
|
++eflg;
|
|
// no break
|
|
case attr_RuntimeVisibleParameterAnnotations:
|
|
pann = &curSeg.annNodes[3];
|
|
eflg |= 2; // flag of secondary loop
|
|
do_annot1:
|
|
if ( apk != attr_parent_kind_method )
|
|
goto declerr_w; // paranoya
|
|
goto do_annot;
|
|
case attr_RuntimeInvisibleAnnotations:
|
|
++eflg;
|
|
// no break
|
|
case attr_RuntimeVisibleAnnotations:
|
|
switch ( apk )
|
|
{
|
|
default: // attr_parent_kind_code
|
|
goto declerr_w; // paranoya
|
|
case attr_parent_kind_class_file:
|
|
pann = curClass.annNodes;
|
|
break;
|
|
case attr_parent_kind_field:
|
|
pann = curField.annNodes;
|
|
break;
|
|
case attr_parent_kind_method:
|
|
pann = curSeg.annNodes;
|
|
break;
|
|
}
|
|
do_annot:
|
|
if ( eflg & 1 )
|
|
++pann; // invisible
|
|
if ( *pann )
|
|
goto duplerr_w;
|
|
temp.create();
|
|
*pann = temp;
|
|
if ( attribute_length == 0 )
|
|
goto declerr_w;
|
|
{
|
|
uchar *p = annotation_realloc(attribute_length);
|
|
r = 1; // no paramsloop
|
|
if ( eflg & 2 ) // Parameters
|
|
{
|
|
r = read1();
|
|
*p++ = (uchar)r;
|
|
--attribute_length;
|
|
if ( !r )
|
|
goto annot_err;
|
|
}
|
|
if ( eflg & 4 ) // defalut
|
|
{
|
|
p = annot_elm(p, &attribute_length);
|
|
if ( p != NULL )
|
|
goto annot_done;
|
|
annot_err:
|
|
temp.kill();
|
|
*pann = (uval_t)-1; // as flag for duplicates
|
|
goto declerr_w;
|
|
}
|
|
do // loop for Parameters
|
|
{
|
|
if ( attribute_length < 2 )
|
|
goto annot_err;
|
|
uint cnt = read2();
|
|
attribute_length -= 2;
|
|
*(ushort *)p = (ushort)cnt;
|
|
p += sizeof(ushort);
|
|
if ( !cnt )
|
|
{
|
|
if ( !(eflg & 2) )
|
|
goto annot_err; // no parameters
|
|
continue;
|
|
}
|
|
eflg |= 8; // flag for parameters
|
|
do
|
|
{
|
|
if ( attribute_length < 2 )
|
|
goto annot_err;
|
|
attribute_length -= 2;
|
|
ushort id = read2();
|
|
if ( !isSingleClass(id) )
|
|
goto annot_err;
|
|
*(ushort*)p = id;
|
|
p = annotation(p + sizeof(ushort), &attribute_length);
|
|
if ( p == NULL )
|
|
goto annot_err;
|
|
}
|
|
while ( --cnt );
|
|
}
|
|
while ( --r ); // parameters loop
|
|
if ( eflg == 2 )
|
|
goto annot_err; // empty Parameters annotation
|
|
annot_done:
|
|
r = (uint)(p - annBuf);
|
|
} // local variable block
|
|
temp.setblob(annBuf, r, 0, BLOB_TAG);
|
|
temp.altset(0, r);
|
|
goto skip_excess;
|
|
|
|
default:
|
|
INTERNAL("load_attributes");
|
|
} // switch
|
|
} // for
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
uchar java_t::CheckSignature(ushort index, attr_parent_kind_t apk)
|
|
{
|
|
char diastr[128];
|
|
const_desc_t opis;
|
|
|
|
if ( !LoadOpis(lm_normal, index, CONSTANT_Utf8, &opis) )
|
|
{
|
|
bad:
|
|
if ( apk != attr_parent_kind_code ) // not debug variable
|
|
load_msg("Invalid signature (#%u)%s\n", index, mk_diag(apk, diastr));
|
|
return 0;
|
|
}
|
|
|
|
CASSERT(HAS_TYPEDSCR < 0x100 && HAS_CALLDSCR < 0x100); //-V590 expression is excessive
|
|
if ( !((uchar)opis.flag & ((apk == attr_parent_kind_method ) ? HAS_CALLDSCR : HAS_TYPEDSCR)) )
|
|
{
|
|
if ( !opis._Ssize )
|
|
goto bad; // PARANOYA
|
|
if ( opis._Sflags & (_OP_EXTSYM_ | _OP_NOSIGN) )
|
|
goto bad;
|
|
opis._Sflags &= (_OP_VALPOS | _OP_METSIGN | _OP_CLSSIGN);
|
|
switch ( apk )
|
|
{
|
|
case attr_parent_kind_method:
|
|
if ( (opis._Sflags &= ~_OP_METSIGN) != _OP_VALPOS )
|
|
goto bad;
|
|
break;
|
|
case attr_parent_kind_class_file:
|
|
opis._Sflags &= ~_OP_CLSSIGN;
|
|
// no break
|
|
default: // FIELD & .var
|
|
if ( opis._Sflags )
|
|
goto bad;
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
// This function should read the input file (it is opened in binary mode)
|
|
// analyze it and:
|
|
// - loading segment and offset are in inf.BaseAddr &
|
|
// - load it into the database using file2base(),mem2base()
|
|
// or allocate addresses by enable_flags() and fill them
|
|
// with values using putByte(), putWord(), putLong()
|
|
// - (if createsegs) create segments using
|
|
// add_segm(segment_t *,const char *name,const char *sclass,int flags)
|
|
// or
|
|
// add_segm(uint short,ea_t,ea_t,char *,char *)
|
|
// see segment.hpp for explanations
|
|
// - set up inf_get_start_ip(),startCS to the starting address
|
|
// - set up inf_get_min_ea(),inf_get_max_ea()
|
|
//
|
|
//
|
|
void java_t::loader(FILE *fp, bool manual)
|
|
{
|
|
ushort j;
|
|
const_desc_t opis;
|
|
|
|
memset(&curClass, 0, sizeof(curClass));
|
|
qfseek(fp, 0, SEEK_SET); // rewind
|
|
FileSize = qfsize(fp);
|
|
myFile = fp;
|
|
enableExt_NameChar();
|
|
|
|
if ( read4() != MAGICNUMBER )
|
|
error("Illegal magic number");
|
|
|
|
curClass.MinVers = read2();
|
|
curClass.MajVers = read2();
|
|
|
|
if ( curClass.MajVers <= JDK_MIN_MAJOR )
|
|
{
|
|
if ( curClass.MajVers < JDK_MIN_MAJOR )
|
|
goto BAD_VERSION;
|
|
curClass.JDKsubver = (uchar)(curClass.MinVers >= JDK_1_1_MINOR);
|
|
}
|
|
else if ( curClass.MajVers > JDK_MAX_MAJOR || curClass.MinVers )
|
|
{
|
|
BAD_VERSION:
|
|
loader_failure("Unsupported file format (version %u.%u)",
|
|
curClass.MajVers, curClass.MinVers);
|
|
}
|
|
else
|
|
{
|
|
curClass.JDKsubver = (uchar)(curClass.MajVers - (JDK_MIN_MAJOR-1));
|
|
}
|
|
|
|
if ( curClass.MajVers >= JDK_SMF_MAJOR_MIN )
|
|
{
|
|
SMF_mode = 1;
|
|
}
|
|
else if ( curClass.JDKsubver <= 1 )
|
|
{
|
|
switch ( curClass.MinVers )
|
|
{
|
|
default:
|
|
ask_for_feedback(
|
|
"Class file with version %u.%u (JDK1.%u?) is not tested!",
|
|
curClass.MajVers, curClass.MinVers, curClass.JDKsubver);
|
|
// no break
|
|
case JDK_1_02_MINOR:
|
|
case JDK_1_1_MINOR:
|
|
break;
|
|
}
|
|
}
|
|
//--
|
|
curClass.maxCPindex = read2();
|
|
if ( curClass.maxCPindex <= 2 )
|
|
loader_failure("Empty constant pool");
|
|
loadMode = loadDialog(manual);
|
|
ConstantNode.create(constant_pool);
|
|
--curClass.maxCPindex; // last valid number
|
|
XtrnNode.create();
|
|
make_NameChars(/*on_load=*/ true); // initialize and set 'load extension'
|
|
const uint nconstants = load_constants_pool();
|
|
if ( !_add_seg(0) )
|
|
{
|
|
XtrnNode.kill();
|
|
}
|
|
else
|
|
{
|
|
curClass.xtrnCnt = ushort(nconstants);
|
|
curClass.xtrnNode = XtrnNode;
|
|
}
|
|
//--
|
|
curClass.AccessFlag = read2();
|
|
if ( curClass.AccessFlag & ~ACC_THIS_MASK )
|
|
load_msg("Illegal class access bits (0x%X)\n", curClass.AccessFlag);
|
|
curClass.This.Dscr = read2();
|
|
if ( LoadNamedClass(lm_normal, curClass.This.Dscr, &opis) )
|
|
{
|
|
curClass.This.Name = opis._name;
|
|
}
|
|
else
|
|
{
|
|
BadRefFile("'this' class", curClass.This.Dscr);
|
|
CASSERT(offsetof(ClassInfo, This.Ref)+2 == offsetof(ClassInfo, This.Dscr)
|
|
&& offsetof(ClassInfo, This.Ref) == offsetof(ClassInfo, This.Name));
|
|
curClass.This.Ref >>= 16;
|
|
// curClass.This.Name = curClass.This.Dscr;
|
|
// curClass.This.Dscr = 0;
|
|
}
|
|
//--
|
|
if ( curClass.xtrnNode )
|
|
setPoolReference();
|
|
curClass.super.Dscr = read2();
|
|
uint i = read2(); // interface counter
|
|
i *= 2;
|
|
if ( FileSize < i )
|
|
errtrunc();
|
|
qfseek(fp, i, SEEK_CUR);
|
|
curClass.FieldCnt = read2();
|
|
qfseek(fp, -2 - qoff64_t(i), SEEK_CUR);
|
|
_add_seg(3); // class segment
|
|
enableExt_NameChar();
|
|
if ( curClass.This.Dscr )
|
|
{
|
|
curSeg.id.Number = 0;
|
|
SetName(curClass.This.Name, curClass.start_ea, curClass.AccessFlag, 0);
|
|
hide_name(curClass.start_ea);
|
|
}
|
|
|
|
if ( curClass.super.Ref )
|
|
{
|
|
if ( !LoadNamedClass(lm_normal, curClass.super.Dscr, &opis) )
|
|
{
|
|
BadRefFile("parent class", curClass.super.Dscr);
|
|
}
|
|
else
|
|
{
|
|
curClass.super.Name = opis._name;
|
|
xtrnRef(curClass.start_ea, opis);
|
|
}
|
|
}
|
|
//--
|
|
if ( (i /= 2) != 0 ) // InterfaceCount
|
|
{
|
|
netnode temp;
|
|
uint r = 0;
|
|
|
|
do
|
|
{
|
|
j = read2();
|
|
if ( j == 0 )
|
|
{
|
|
load_msg("Ignore zero interface index\n");
|
|
continue;
|
|
}
|
|
if ( !r )
|
|
temp.create();
|
|
|
|
uint32 refd = (uint32)j << 16;
|
|
if ( !LoadNamedClass(lm_normal, j, &opis) )
|
|
{
|
|
BadRefFile("interface", j);
|
|
}
|
|
else
|
|
{
|
|
xtrnRef(curClass.start_ea, opis);
|
|
refd |= opis._name;
|
|
}
|
|
temp.altset(++r, refd);
|
|
}
|
|
while ( --i );
|
|
if ( r )
|
|
{
|
|
temp.altset(0, r);
|
|
curClass.impNode = temp;
|
|
}
|
|
}
|
|
//---
|
|
ClassNode.create();
|
|
curClass.ClassNode = ClassNode;
|
|
qfseek(fp, 2, SEEK_CUR);
|
|
if ( errload )
|
|
{
|
|
int f = (curClass.AccessFlag & ~ACC_THIS_MASK) ? curClass.AccessFlag : 0;
|
|
mark_access(curClass.start_ea, f);
|
|
}
|
|
//---
|
|
for ( i = 1; (ushort)i <= curClass.FieldCnt; i++ )
|
|
{
|
|
memset(&curField, 0, sizeof(curField));
|
|
curField.id.Number = (ushort)i;
|
|
curField.id.access = read2();
|
|
j = curField.id.access & ACC_ACCESS_MASK;
|
|
if ( (curField.id.access & ~ACC_FIELD_MASK) || (j & (j-1)) )
|
|
{
|
|
load_msg("Illegal Field#%u Attribute 0x%04x\n", i, curField.id.access);
|
|
// curField.id.extflg |= EFL_ACCESS;
|
|
mark_access(curClass.start_ea + i, curField.id.access);
|
|
}
|
|
|
|
curField.id.name = read2();
|
|
if ( !CheckFieldName(lm_normal, curField.id.name, NULL) )
|
|
curField.id.extflg |= EFL_NAME;
|
|
|
|
curField.id.dscr = read2();
|
|
if ( !CheckFieldDscr(lm_normal, curField.id.dscr, &opis) )
|
|
curField.id.extflg |= EFL_TYPE;
|
|
else
|
|
xtrnRef_dscr(curClass.start_ea + curField.id.Number, &opis);
|
|
if ( curField.id.extflg & EFL_NAMETYPE )
|
|
load_msg("Illegal NameAndType of field %u\n", i);
|
|
load_attributes(attr_parent_kind_field);
|
|
for ( int n = 0; n < qnumber(curField.annNodes); n++ )
|
|
if ( curField.annNodes[n] == (uval_t)-1 )
|
|
++curField.annNodes[n];
|
|
ClassNode.supset(i, &curField, sizeof(curField));
|
|
if ( !(curField.id.extflg & EFL_NAME) )
|
|
SetName(curField.id.name, curClass.start_ea + i, curField.id.access, i);
|
|
}
|
|
//--
|
|
curClass.MethodCnt = read2();
|
|
for ( i = 1; i <= curClass.MethodCnt; i++ )
|
|
{
|
|
memset(&curSeg, 0, sizeof(curSeg));
|
|
curSeg.id.Number = (ushort)i;
|
|
curSeg.id.access = read2();
|
|
j = curSeg.id.access & ACC_ACCESS_MASK;
|
|
if ( !(j & ~ACC_METHOD_MASK) && !(j & (j-1)) )
|
|
j = 0;
|
|
else
|
|
load_msg("Illegal Method#%u Attribute 0x%04x\n", i, curSeg.id.access);
|
|
// curSeg.id.extflg |= EFL_ACCESS;
|
|
|
|
_add_seg(1); // create code segment // this for strnRef_dscr
|
|
curSeg.id.name = read2();
|
|
if ( !CheckFieldName(lm_normal, curSeg.id.name, NULL) )
|
|
curSeg.id.extflg |= EFL_NAME;
|
|
|
|
curSeg.id.dscr = read2();
|
|
if ( !CheckCallDscr(lm_normal, curSeg.id.dscr, &opis) )
|
|
curSeg.id.extflg |= EFL_TYPE;
|
|
else
|
|
xtrnRef_dscr(curSeg.start_ea, &opis, 1);
|
|
if ( curSeg.id.extflg & EFL_NAMETYPE )
|
|
load_msg("Illegal NameAndType of method %u\n", i);
|
|
// if ( curSeg.id.extflg & EFL_ACCESS )
|
|
if ( j )
|
|
mark_access(curSeg.start_ea, curSeg.id.access);
|
|
load_attributes(attr_parent_kind_method);
|
|
if ( curSeg.smNode == BADNODE )
|
|
curSeg.smNode = 0; // remove 'flagged' value
|
|
for ( int n = 0; n < qnumber(curSeg.annNodes); n++ )
|
|
if ( curSeg.annNodes[n] == (uval_t)-1 )
|
|
++curSeg.annNodes[n];
|
|
if ( curSeg.varNode )
|
|
resizeLocVars();
|
|
if ( curSeg.thrNode )
|
|
{
|
|
netnode tnode(curSeg.thrNode);
|
|
if ( !tnode.altval(0) )
|
|
{
|
|
tnode.kill();
|
|
curSeg.thrNode = 0;
|
|
}
|
|
}
|
|
ClassNode.supset(-(int)i, &curSeg, sizeof(curSeg));
|
|
}
|
|
//--
|
|
load_attributes(attr_parent_kind_class_file); // Source File
|
|
for ( int n = 0; n < qnumber(curClass.annNodes); n++ )
|
|
if ( curClass.annNodes[n] == (uval_t)-1 )
|
|
++curClass.annNodes[n];
|
|
myFile = NULL;
|
|
if ( curClass.encClass == 0xFFFF )
|
|
++curClass.encClass; // unification in out
|
|
if ( FileSize )
|
|
warning("This file has extra information (pos=0x%" FMT_64 "x)", qftell(fp));
|
|
//---
|
|
CheckPoolReference(0);
|
|
endLoad_NameChar(); // set 'standart extension'
|
|
if ( !set_parent_object() && (curClass.AccessFlag & ACC_INTERFACE) )
|
|
{
|
|
load_msg("This is interface, but superclass is not java.lang.Object!\n");
|
|
mark_and_comment(curClass.start_ea, "Interface have nonstandart parent");
|
|
}
|
|
if ( curClass.impNode && !curClass.super.Ref )
|
|
{
|
|
load_msg("This have implements without superclass!\n");
|
|
mark_and_comment(curClass.start_ea, "Empty supperclass not for Object");
|
|
}
|
|
if ( errload )
|
|
curClass.extflg |= XFL_C_ERRLOAD;
|
|
ConstantNode.supset(CNS_CLASS, &curClass, sizeof(curClass)); // load end!
|
|
//--
|
|
debugmode = 1; // full pass...
|
|
for ( i = 1; i <= curClass.MethodCnt; i++ )
|
|
{
|
|
msg("Analysing method %u...\n", i);
|
|
ClassNode.supval(-(int)i, &curSeg, sizeof(curSeg));
|
|
ea_t ea = curSeg.start_ea;
|
|
show_addr(ea);
|
|
ea_t end = ea + curSeg.CodeSize;
|
|
segment_t *s = getseg(ea);
|
|
if ( s == NULL || end < ea || end > s->end_ea )
|
|
loader_failure("Bad method %u code size 0x%X", i, curSeg.CodeSize);
|
|
if ( !curSeg.CodeSize )
|
|
{
|
|
create_byte(ea, 0x10);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
int sz = create_insn(ea);
|
|
if ( sz == 0 )
|
|
++sz;
|
|
ea += sz;
|
|
}
|
|
while ( ea < end );
|
|
}
|
|
if ( (curSeg.id.extflg & EFL_NAME) == 0 )
|
|
SetName(curSeg.id.name, curSeg.start_ea, curSeg.id.access,
|
|
curClass.FieldCnt + i);
|
|
add_func(curSeg.start_ea, end + 1);
|
|
}
|
|
debugmode = 0; // all references setting...
|
|
CheckPoolReference(1);
|
|
ConstantNode.altset(CNA_VERSION, _CUR_IDP_VER);
|
|
ResW_newbase();
|
|
create_filename_cmt();
|
|
}
|