#include "java.hpp" #include "oututil.hpp" //-------------------------------------------------------------------------- // returns number of positions advanced int out_java_t::out_commented(const char *p, color_t color) { if ( color != COLOR_NONE ) out_tagon(color); size_t inplen = outbuf.length(); out_printf("%s %s", ash.cmnt, p); int npos = outbuf.length() - inplen; if ( color != COLOR_NONE ) out_tagoff(color); return npos; } //---------------------------------------------------------------------- bool out_java_t::change_line(bool main) { bool overflow = false; if ( pm().g_bufinited ) { pm().outcnt = 0; uchar sv = inf_get_indent(); inf_set_indent((uchar)pm().curpos); overflow = flush_buf(outbuf.c_str(), main ? -1 : pm().curpos); inf_set_indent(sv); // for autocomment with call fmtName outbuf.qclear(); outbuf.reserve(pm().g_bufsize); } return overflow; } //---------------------------------------------------------------------- size_t out_java_t::putLine(java_t &pm) { color_t color = COLOR_NONE; if ( pm.g_bufinited ) { const char *p = strrchr(outbuf.c_str(), COLOR_ON); if ( p != NULL && p[1] && strchr(p+2, COLOR_OFF) == NULL ) // second - PARANOYA { color = (color_t)*(p + 1); out_tagoff(color); } } out_symbol('\\'); if ( change_line(pm.curpos != 0 && !pm.no_prim) ) return 0; pm.curpos = 0; if ( color != COLOR_NONE ) out_tagon(color); pm.ref_pos = outbuf.length(); return pm.maxpos; } //---------------------------------------------------------------------- bool out_java_t::checkLine(size_t size) { if ( !pm().g_bufinited ) return true; if ( pm().maxpos - pm().curpos > pm().outcnt + size ) return true; return putLine(pm()) != 0; } //---------------------------------------------------------------------- bool out_java_t::chkOutLine(const char *str, size_t len) { if ( !checkLine(len) ) return true; pm().outcnt += len; out_line(str); return false; } //---------------------------------------------------------------------- bool out_java_t::chkOutKeyword(const char *str, uint len) { if ( !checkLine(len) ) return true; OutKeyword(str, len); return false; } //---------------------------------------------------------------------- bool out_java_t::chkOutSymbol(char c) { if ( !checkLine(1) ) return true; ++pm().outcnt; out_symbol(c); return false; } //---------------------------------------------------------------------- bool out_java_t::chkOutChar(char c) { if ( !checkLine(1) ) return true; ++pm().outcnt; out_char(c); return false; } //---------------------------------------------------------------------- bool out_java_t::chkOutSymSpace(char c) { if ( !checkLine(2) ) return true; out_symbol(c); out_char(' '); pm().outcnt += 2; return false; } //---------------------------------------------------------------------- uchar out_java_t::putShort(ushort value, uchar wsym) { size_t inplen = outbuf.length(); out_tagon(COLOR_ERROR); if ( wsym ) out_char(wsym); out_btoa(value, #ifdef __debug__ debugmode ? 16 : #endif 10); out_tagoff(COLOR_ERROR); char tmpstr[32]; size_t curlen = outbuf.length(); size_t len = curlen - inplen; qstrncpy(tmpstr, &outbuf[inplen], qmin(len+1, sizeof(tmpstr))); outbuf.resize(inplen); return chkOutLine(tmpstr, tag_strlen(tmpstr)); } //---------------------------------------------------------------------- char out_java_t::outName(ea_t from, int n, ea_t ea, uval_t off, uchar *rbad) { qstring qbuf; if ( get_name_expr(&qbuf, from, n, ea + off, off) <= 0 ) { remember_problem(PR_NONAME, insn.ea); return 0; } if ( chkOutLine(qbuf.begin(), tag_strlen(qbuf.begin())) ) { *rbad = 1; return 0; } return 1; } //--------------------------------------------------------------------------- uchar out_java_t::putVal(const op_t &x, uchar mode, uchar warn) { size_t inplen = outbuf.length(); { flags_t saved = F; F = 0; out_value(x, mode); F = saved; } char str[MAXSTR]; size_t curlen = outbuf.length(); size_t len = curlen - inplen; qstrncpy(str, &outbuf[inplen], qmin(len+1, sizeof(str))); outbuf.resize(inplen); if ( warn ) out_tagon(COLOR_ERROR); if ( warn ) { qstring qstr; len = tag_remove(&qstr, str); qstrncpy(str, qstr.c_str(), sizeof(str)); } else { len = tag_strlen(str); } if ( chkOutLine(str, len) ) return 0; if ( warn ) out_tagoff(COLOR_ERROR); return 1; } //---------------------------------------------------------------------- CASSERT(MIN_ARG_SIZE >= 2 && MIN_ARG_SIZE < 30); uchar out_java_t::OutUtf8(ushort index, fmt_t mode, color_t color) { size_t size = (pm().maxpos - pm().curpos) - pm().outcnt; if ( (int)size <= MIN_ARG_SIZE ) { DEB_ASSERT(((int)size < 0), "OutUtf8"); size = putLine(pm()); if ( size == 0 ) return 1; } if ( color != COLOR_NONE ) out_tagon(color); pm().ref_pos = outbuf.length(); struct ida_local lambda_t { static size_t call_putLine(java_t &pm, out_java_t *oj) { return oj->putLine(pm); } }; if ( fmtString(pm(), index, size, mode, lambda_t::call_putLine) < 0 ) return 1; pm().outcnt += outbuf.length() - pm().ref_pos; if ( color != COLOR_NONE ) out_tagoff(color); return 0; } //--------------------------------------------------------------------------- uchar out_java_t::out_index(ushort index, fmt_t mode, color_t color, uchar as_index) { if ( as_index ) { if ( !(pm().idpflags & (IDM_BADIDXSTR | IDM_OUTASM)) // no store in file || !pm().is_valid_string_index(index) ) { return putShort(index); } color = COLOR_ERROR; mode = fmt_string; } return OutUtf8(index, mode, color); } //-------------------------------------------------------------------------- uchar out_java_t::out_alt_ind(uint32 val) { if ( (ushort)val ) return OutUtf8((ushort)val, fmt_fullname, COLOR_IMPNAME); return putShort((ushort)(val >> 16)); } //-------------------------------------------------------------------------- // special label format/scan procedures //-------------------------------------------------------------------------- void out_java_t::out_method_label(uchar is_end) { set_gen_cmt(true); set_gen_xrefs(true); gen_printf(0, COLSTR("met%03u_%s%s", SCOLOR_CODNAME), pm().curSeg.id.Number, is_end ? "end" : "begin", COLSTR(":", SCOLOR_SYMBOL)); } //--------------------------------------------------------------------------- char out_java_t::putMethodLabel(ushort off) { char str[32]; int len = qsnprintf(str, sizeof(str), "met%03u_%s", pm().curSeg.id.Number, off ? "end" : "begin"); if ( !checkLine(len) ) return 1; out_tagon(COLOR_CODNAME); outLine(str, len); out_tagoff(COLOR_CODNAME); return 0; } //-------------------------------------------------------------------------- // procedure for get_ref_addr ssize_t java_t::check_special_label(const char *buf, size_t len) const { if ( len >= sizeof("met000_end")-1 && (*(uint32*)buf & 0xFFFFFF) == ('m'|('e'<<8)|('t'<<16)) ) { switch ( *(uint32*)&buf[len -= 4] ) { case ('_'|('e'<<8)|('n'<<16)|('d'<<24)): break; case ('e'|('g'<<8)|('i'<<16)|('n'<<24)): if ( len >= sizeof("met000_begin")-1 - 4 && *(ushort*)&buf[len -= 2] == ('_'|('b'<<8)) ) { break; } // no break default: len |= -1; // as flag break; } if ( len <= sizeof("met00000")-1 ) { size_t off = curSeg.CodeSize; if ( buf[len+1] == 'b' ) off = 0; size_t n = 0; size_t j = sizeof("met")-1; while ( true ) { if ( !qisdigit((uchar)buf[j]) ) break; n = n*10 + (buf[j] - '0'); if ( ++j == len ) { if ( n >= 0x10000 || (ushort)n != curSeg.id.Number ) break; return off; } } } } return -1; } //-------------------------------------------------------------------------- // end of special-label procedures //---------------------------------------------------------------------- uchar out_java_t::outOffName(ushort off) { if ( !off || off == pm().curSeg.CodeSize ) return putMethodLabel(off); if ( off < pm().curSeg.CodeSize ) { uchar err = 0; if ( outName(pm().curSeg.start_ea + pm().curSeg.CodeSize, 0, pm().curSeg.start_ea, off, &err) ) return 0; // good if ( err ) return 1; // bad } return putShort(off, 0); } //---------------------------------------------------------------------- bool out_java_t::block_begin(uchar off) { return flush_buf(COLSTR("{", SCOLOR_SYMBOL), off); } //---------------------------------------------------------------------- bool out_java_t::block_end(uint32 off) { return flush_buf(COLSTR("}", SCOLOR_SYMBOL), off); } //---------------------------------------------------------------------- bool out_java_t::block_close(uint32 off, const char *name) { if ( !jasmin() ) return block_end(off); return gen_printf(off, COLSTR(".end %s", SCOLOR_KEYWORD), name); } //---------------------------------------------------------------------- bool out_java_t::close_comment(void) { return flush_buf(COLSTR("*/", SCOLOR_AUTOCMT), 0); } //--------------------------------------------------------------------------- uchar out_java_t::out_nodelist(uval_t nodeid, uchar pos, const char *pref) { netnode node(nodeid); uval_t cnt = node.altval(0); if ( cnt == 0 ) DESTROYED("out::nodelist"); uval_t off = 0; if ( pref ) // jasmin { if ( change_line() ) { bad: return 0; } off = strlen(pref); } uint i = 0; while ( true ) { if ( pref ) // jasmin (single directive per line) { pm().curpos = pos; out_keyword(pref); pm().outcnt = off; } else if ( i && chkOutSymSpace(',') ) { goto bad; // prompted list } if ( out_alt_ind((uint32)node.altval(++i)) ) goto bad; if ( i >= cnt ) return 1; if ( pref && change_line() ) goto bad; // jasmin } } //---------------------------------------------------------------------- void out_java_t::init_prompted_output(uchar pos) { pm().maxpos = inf_get_margin(); // if ( maxpos < 32 ) // maxpos = 32; // if ( maxpos > MAXSTR - 4 ) // maxpos = MAXSTR - 4; #ifdef __debug__ if ( debugmode == -1 && inf.show_line_pref() && inf_get_margin() == 77 && !inf.bin_prefix_size ) { maxpos -= gl_psize; } #endif pm().g_bufsize = (MAXSTR*2) - STR_PRESERVED; pm().g_bufinited = true; outbuf.qclear(); outbuf.reserve(pm().g_bufsize); pm().curpos = pos; pm().outcnt = 0; } //---------------------------------------------------------------------- void out_java_t::term_prompted_output(void) { outbuf.qclear(); pm().g_bufinited = false; pm().g_bufsize = 0; pm().maxpos = 0; pm().curpos = -1; } //---------------------------------------------------------------------- uchar out_java_t::OutConstant(const op_t &_x, bool include_descriptor) { op_t x = _x; fmt_t fmt = fmt_FieldDescriptor; color_t color; insn_t cur_insn; decode_insn(&cur_insn, insn_ea); switch ( (uchar)x.cp_type ) { default: warning("OC: bad constant type %u", (uchar)x.cp_type); break; case CONSTANT_Long: x.dtype = dt_qword; goto outNum; case CONSTANT_Double: x.dtype = dt_double; goto outNum; case CONSTANT_Integer: x.dtype = dt_dword; goto outNum; case CONSTANT_Float: x.dtype = dt_float; outNum: if ( putVal(x, OOF_NUMBER | OOF_SIGNED | OOFW_IMM, 0) ) break; badconst: return 0; case CONSTANT_Utf8: if ( OutUtf8(x.cp_ind, fmt_string, COLOR_STRING) ) goto badconst; break; case CONSTANT_String: if ( OutUtf8(x._name, fmt_string, COLOR_STRING) ) goto badconst; break; case CONSTANT_NameAndType: nameandtype: if ( OutUtf8(x._class, fmt_fullname) || OutUtf8(x._name, fmt_fullname) ) { goto badconst; } break; case CONSTANT_InvokeDynamic: { const_desc_t invdyn; if ( pm().ConstantNode.supval(x.cp_ind, &invdyn, sizeof(invdyn)) != sizeof(invdyn) ) goto badconst; // Retrieve NameAndType const_desc_t nat; if ( pm().ConstantNode.supval(invdyn._name, &nat, sizeof(nat)) != sizeof(nat) ) goto badconst; memset(&x, 0, sizeof(x)); copy_const_to_opnd(x, nat); x.ref = 0; x.cp_type = nat.type; x.cp_ind = invdyn._name; goto nameandtype; } break; case CONSTANT_MethodHandle: { const_desc_t tmp; if ( pm().ConstantNode.supval(x._mhr_index, &tmp, sizeof(tmp)) != sizeof(tmp) ) goto badconst; op_t tmpop; memset(&tmpop, 0, sizeof(tmpop)); copy_const_to_opnd(tmpop, tmp); tmpop.ref = 0; // as flag tmpop.cp_type = tmp.type; tmpop.cp_ind = x._mhr_index; OutConstant(tmpop, include_descriptor); } break; case CONSTANT_MethodType: if ( OutUtf8(x._mtd_index, fmt_fullname, COLOR_KEYWORD) ) goto badconst; break; case CONSTANT_Class: CASSERT((fmt_ClassName_or_Array+1) == fmt_ClassName && (fmt_ClassName+1) == fmt_fullname); { fmt_t f2 = (fmt_t )x.addr_shorts.high; color_t c2 = f2 < fmt_ClassName_or_Array || f2 > fmt_fullname ? COLOR_KEYWORD : cur_insn.xtrn_ip == 0xFFFF ? COLOR_DNAME : COLOR_IMPNAME; if ( OutUtf8(x._name, f2, c2) ) goto badconst; } break; case CONSTANT_InterfaceMethodref: case CONSTANT_Methodref: fmt = fmt_method_ReturnType; // fallthrough case CONSTANT_Fieldref: #ifdef VIEW_WITHOUT_TYPE if ( include_descriptor ) #endif if ( !jasmin() && OutUtf8(x._dscr, fmt, COLOR_KEYWORD) ) goto badconst; color = x._class == pm().curClass.This.Dscr ? COLOR_DNAME : COLOR_IMPNAME; out_tagon(color); if ( jasmin() || (color == COLOR_IMPNAME && !include_descriptor) ) // other class { if ( OutUtf8(x._name, fmt_ClassName) || chkOutDot() ) goto badconst; } if ( OutUtf8(x._subnam, fmt_UnqualifiedName) ) goto badconst; // Field out_tagoff(color); if ( jasmin() ) { if ( fmt == fmt_method_ReturnType ) fmt = fmt_FieldDescriptor_nospace; // no space at end else if ( chkOutSpace() ) goto badconst; } else { if ( fmt != fmt_method_ReturnType ) break; fmt = fmt_method_TypeSignature; } if ( OutUtf8(x._dscr, fmt, COLOR_KEYWORD) ) goto badconst; break; } return 1; } //-------------------------------------------------------------------------- // FIXME: there should be a better way of suppressing borders in disassembly void out_java_t::myBorder(void) { gen_empty_line(); if ( pm().user_limiter ) { inf_set_limiter(LMT_THIN); gen_border_line(false); } inf_set_limiter(0); // do not output border between method & vars :( } //-------------------------------------------------------------------------- uchar out_java_t::out_problems(char str[MAXSTR], const char *prefix) { if ( pm().curClass.extflg & XFL_C_ERRLOAD ) { myBorder(); gen_printf(DEFAULT_INDENT, COLSTR("%s This class has had loading time problem(s)", SCOLOR_ERROR), prefix); if ( pm().curClass.msgNode ) { gen_empty_line(); if ( pm().print_loader_messages(str, prefix, this) == -1 ) return 1; } myBorder(); } return 0; } //-------------------------------------------------------------------------- uchar out_java_t::putScope(ushort scope, uint32 doff) { if ( !scope || scope == pm().curSeg.CodeSize ) return putMethodLabel(scope); if ( scope < pm().curSeg.CodeSize ) { uchar err = 0; if ( outName(pm().curSeg.DataBase + doff, 0, pm().curSeg.start_ea, scope, &err) ) return 0; if ( err ) return 1; } return putShort(scope, 0); } //---------------------------------------------------------------------- size_t out_java_t::debLine(java_t &) { out_char('"'); out_tagoff(COLOR_STRING); if ( change_line() ) return 0; return putDeb(1); } //---------------------------------------------------------------------- void out_java_t::OutKeyword(const char *str, size_t len) { pm().outcnt += len; out_keyword(str); } //---------------------------------------------------------------------- void out_java_t::outLine(const char *str, uint len) { pm().outcnt += len; out_line(str); } //---------------------------------------------------------------------- uchar out_java_t::chkOutDot(void) { return chkOutChar('.'); } //---------------------------------------------------------------------- void out_java_t::OutSpace(void) { ++pm().outcnt; out_char(' '); } //---------------------------------------------------------------------- uchar out_java_t::chkOutSpace(void) { return chkOutChar(' '); } //-------------------------------------------------------------------------- size_t out_java_t::putDeb(uchar next) { OUT_KEYWORD(".debug "); out_tagon(COLOR_STRING); if ( next ) out_char('"'); return pm().maxpos - pm().outcnt; } //---------------------------------------------------------------------- bool out_java_t::out_operand(const op_t &x) { int outf; uchar warn = 0; switch ( x.type ) { case o_near: if ( x.ref ) { ++warn; } else { if ( outName(insn.ea + x.offb, x.n, pm().curSeg.start_ea, x.addr, &warn) ) break; if ( warn ) goto badop; } if ( putVal(x, OOF_ADDR | OOF_NUMBER | OOFS_NOSIGN | OOFW_32, warn) ) break; // no break case o_void: badop: return false; case o_imm: if ( x.ref == 2 ) ++warn; outf = OOFW_IMM | OOF_NUMBER | (x.ref ? OOFS_NOSIGN : OOF_SIGNED); if ( putVal(x, outf, warn) ) break; goto badop; case o_mem: if ( jasmin() ) goto putidcv_num; if ( x.ref ) { putAddr: ++warn; } else { if ( outName(insn.ea + x.offb, x.n, pm().curSeg.DataBase, x.addr, &warn) ) break; if ( warn ) goto badop; } putidcv_num: if ( putVal(x, OOF_ADDR | OOF_NUMBER | OOFS_NOSIGN | OOFW_16, warn) ) break; goto badop; case o_cpool: if ( !x.cp_ind ) { OUT_KEYWORD("NULL"); } else { if ( x.ref ) goto putAddr; if ( !OutConstant(x) ) goto badop; } break; case o_array: if ( !x.ref ) { uchar btype = 0; switch ( uchar(x.cp_type) ) { case T_BOOLEAN: btype = j_bool; break; case T_CHAR: btype = j_char; break; case T_FLOAT: btype = j_float; break; case T_DOUBLE: btype = j_double; break; case T_BYTE: btype = j_byte; break; case T_SHORT: btype = j_short; break; case T_INT: btype = j_int; break; case T_LONG: btype = j_long; break; } const TXS *tname = get_base_typename(btype); if ( tname == 0 || chkOutKeyword(tname->str, tname->size) ) goto badop; } else { static const char tt_bogust[] = "BOGUST_TYPE-"; if ( !checkLine(sizeof(tt_bogust) + 2) ) goto badop; out_tagon(COLOR_ERROR); size_t inplen = outbuf.length(); out_printf("%c%s%u", WARN_SYM, tt_bogust, (uchar)x.cp_type); pm().outcnt += outbuf.length() - inplen; out_tagoff(COLOR_ERROR); } break; default: warning("out: %a: bad optype %d", insn.ip, x.type); break; } return true; } //-------------------------------------------------------------------------- void java_t::java_footer(outctx_t &ctx) { if ( !jasmin() ) { out_java_t *p = (out_java_t *)&ctx; p->block_end(0); } }