ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/printjava.cc
2023-09-12 17:43:02 +00:00

365 lines
9.6 KiB
C++

/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "printjava.hh"
#include "funcdata.hh"
namespace ghidra {
OpToken PrintJava::instanceof = { "instanceof", "", 2, 60, true, OpToken::binary, 1, 0, (OpToken *)0 };
// Constructing this registers the capability
PrintJavaCapability PrintJavaCapability::printJavaCapability;
PrintJavaCapability::PrintJavaCapability(void)
{
name = "java-language";
isdefault = false;
}
PrintLanguage *PrintJavaCapability::buildLanguage(Architecture *glb)
{
return new PrintJava(glb,name);
}
PrintJava::PrintJava(Architecture *glb,const string &nm) : PrintC(glb,nm)
{
resetDefaultsPrintJava();
nullToken = "null"; // Java standard lower-case 'null'
if (castStrategy != (CastStrategy *)0)
delete castStrategy;
castStrategy = new CastStrategyJava();
}
void PrintJava::resetDefaults(void)
{
PrintC::resetDefaults();
resetDefaultsPrintJava();
}
void PrintJava::docFunction(const Funcdata *fd)
{
bool singletonFunction = false;
if (curscope == (const Scope *)0) {
singletonFunction = true;
// Always assume we are in the scope of the parent class
pushScope(fd->getScopeLocal()->getParent());
}
PrintC::docFunction(fd);
if (singletonFunction)
popScope();
}
/// Print a data-type up to the identifier, store off array sizes
/// for printing after the identifier. Find the root type (the one with an identifier)
/// and the count number of wrapping arrays.
/// \param ct is the given data-type
/// \param noident is \b true if no identifier will be pushed with this declaration
void PrintJava::pushTypeStart(const Datatype *ct,bool noident)
{
int4 arrayCount = 0;
for(;;) {
if (ct->getMetatype() == TYPE_PTR) {
if (isArrayType(ct))
arrayCount += 1;
ct = ((TypePointer *)ct)->getPtrTo();
}
else if (ct->getName().size() != 0)
break;
else {
ct = glb->types->getTypeVoid();
break;
}
}
OpToken *tok;
if (noident)
tok = &type_expr_nospace;
else
tok = &type_expr_space;
pushOp(tok,(const PcodeOp *)0);
for(int4 i=0;i<arrayCount;++i)
pushOp(&subscript,(const PcodeOp *)0);
if (ct->getName().size()==0) { // Check for anonymous type
// We could support a struct or enum declaration here
string nm = genericTypeName(ct);
pushAtom(Atom(nm,typetoken,EmitMarkup::type_color,ct));
}
else {
pushAtom(Atom(ct->getDisplayName(),typetoken,EmitMarkup::type_color,ct));
}
for(int4 i=0;i<arrayCount;++i)
pushAtom(Atom(EMPTY_STRING,blanktoken,EmitMarkup::no_color)); // Fill in the blank array index
}
void PrintJava::pushTypeEnd(const Datatype *ct)
{ // This routine doesn't have to do anything
}
void PrintJava::adjustTypeOperators(void)
{
scope.print1 = ".";
shift_right.print1 = ">>>";
TypeOp::selectJavaOperators(glb->inst,true);
}
/// References to java array objects where the underlying element is a java primitive look like:
/// - Pointer to int
/// - Pointer to bool
/// - Pointer to float
///
/// An array of java class objects is represented as a pointer to pointer data-type.
/// \param ct is the given data-type
/// \return \b true if the data-type references a java array object
bool PrintJava::isArrayType(const Datatype *ct)
{
if (ct->getMetatype() != TYPE_PTR) // Java arrays are always Ghidra pointer types
return false;
ct = ((TypePointer *)ct)->getPtrTo();
switch(ct->getMetatype()) {
case TYPE_UINT: // Pointer to unsigned is placeholder for class reference, not an array
if (ct->isCharPrint())
return true;
break;
case TYPE_INT:
case TYPE_BOOL:
case TYPE_FLOAT: // Pointer to primitive type is an array
case TYPE_PTR: // Pointer to class reference is an array
return true;
default:
break;
}
return false;
}
void PrintJava::resetDefaultsPrintJava(void)
{
option_NULL = true; // Automatically use 'null' token
option_convention = false; // Automatically hide convention name
mods |= hide_thisparam; // turn on hiding of 'this' parameter
}
/// Assuming the given Varnode is a dereferenced pointer, determine whether
/// it needs to be represented using '[0]' syntax.
/// \param vn is the given Varnode
/// \return \b true if '[0]' syntax is required
bool PrintJava::needZeroArray(const Varnode *vn)
{
if (!isArrayType(vn->getType()))
return false;
if (vn->isExplicit()) return true;
if (!vn->isWritten()) return true;
OpCode opc = vn->getDef()->code();
if ((opc == CPUI_PTRADD)||(opc == CPUI_PTRSUB)||(opc == CPUI_CPOOLREF))
return false;
return true;
}
void PrintJava::printUnicode(ostream &s,int4 onechar) const
{
if (unicodeNeedsEscape(onechar)) {
switch(onechar) { // Special escape characters
case 0:
s << "\\0";
return;
case 8:
s << "\\b";
return;
case 9:
s << "\\t";
return;
case 10:
s << "\\n";
return;
case 12:
s << "\\f";
return;
case 13:
s << "\\r";
return;
case 92:
s << "\\\\";
return;
case '"':
s << "\\\"";
return;
case '\'':
s << "\\\'";
return;
}
// Generic unicode escape
if (onechar < 65536) {
s << "\\ux" << setfill('0') << setw(4) << hex << onechar;
}
else
s << "\\ux" << setfill('0') << setw(8) << hex << onechar;
return;
}
StringManager::writeUtf8(s, onechar); // Emit normally
}
void PrintJava::opLoad(const PcodeOp *op)
{
uint4 m = mods | print_load_value;
bool printArrayRef = needZeroArray(op->getIn(1));
if (printArrayRef)
pushOp(&subscript,op);
pushVn(op->getIn(1),op,m);
if (printArrayRef)
push_integer(0,4,false,syntax,(Varnode *)0,op);
}
void PrintJava::opStore(const PcodeOp *op)
{
uint4 m = mods | print_store_value; // Inform sub-tree that we are storing
pushOp(&assignment,op); // This is an assignment
if (needZeroArray(op->getIn(1))) {
pushOp(&subscript,op);
pushVn(op->getIn(1),op,m);
push_integer(0,4,false,syntax,(Varnode *)0,op);
pushVn(op->getIn(2),op,mods);
}
else {
// implied vn's pushed on in reverse order for efficiency
// see PrintLanguage::pushVnImplied
pushVn(op->getIn(2),op,mods);
pushVn(op->getIn(1),op,m);
}
}
void PrintJava::opCallind(const PcodeOp *op)
{
pushOp(&function_call,op);
const Funcdata *fd = op->getParent()->getFuncdata();
FuncCallSpecs *fc = fd->getCallSpecs(op);
if (fc == (FuncCallSpecs *)0)
throw LowlevelError("Missing indirect function callspec");
int4 skip = getHiddenThisSlot(op, fc);
int4 count = op->numInput() - 1;
count -= (skip < 0) ? 0 : 1;
if (count > 1) { // Multiple parameters
pushVn(op->getIn(0),op,mods);
for(int4 i=0;i<count-1;++i)
pushOp(&comma,op);
// implied vn's pushed on in reverse order for efficiency
// see PrintLanguage::pushVnImplied
for(int4 i=op->numInput()-1;i>=1;--i) {
if (i == skip) continue;
pushVn(op->getIn(i),op,mods);
}
}
else if (count == 1) { // One parameter
if (skip == 1)
pushVn(op->getIn(2),op,mods);
else
pushVn(op->getIn(1),op,mods);
pushVn(op->getIn(0),op,mods);
}
else { // A void function
pushVn(op->getIn(0),op,mods);
pushAtom(Atom(EMPTY_STRING,blanktoken,EmitMarkup::no_color));
}
}
void PrintJava::opCpoolRefOp(const PcodeOp *op)
{
const Varnode *outvn = op->getOut();
const Varnode *vn0 = op->getIn(0);
vector<uintb> refs;
for(int4 i=1;i<op->numInput();++i)
refs.push_back(op->getIn(i)->getOffset());
const CPoolRecord *rec = glb->cpool->getRecord(refs);
if (rec == (const CPoolRecord *)0) {
pushAtom(Atom("UNKNOWNREF",syntax,EmitMarkup::const_color,op,outvn));
}
else {
switch(rec->getTag()) {
case CPoolRecord::string_literal:
{
ostringstream str;
int4 len = rec->getByteDataLength();
if (len > 2048)
len = 2048;
str << '\"';
escapeCharacterData(str,rec->getByteData(),len,1,false);
if (len == rec->getByteDataLength())
str << '\"';
else {
str << "...\"";
}
pushAtom(Atom(str.str(),vartoken,EmitMarkup::const_color,op,outvn));
break;
}
case CPoolRecord::class_reference:
pushAtom(Atom(rec->getToken(),vartoken,EmitMarkup::type_color,op,outvn));
break;
case CPoolRecord::instance_of:
{
Datatype *dt = rec->getType();
while(dt->getMetatype() == TYPE_PTR) {
dt = ((TypePointer *)dt)->getPtrTo();
}
pushOp(&instanceof,op);
pushVn(vn0,op,mods);
pushAtom(Atom(dt->getDisplayName(),syntax,EmitMarkup::type_color,op,outvn));
break;
}
case CPoolRecord::primitive: // Should be eliminated
case CPoolRecord::pointer_method:
case CPoolRecord::pointer_field:
case CPoolRecord::array_length:
case CPoolRecord::check_cast:
default:
{
Datatype *ct = rec->getType();
EmitMarkup::syntax_highlight color = EmitMarkup::var_color;
if (ct->getMetatype() == TYPE_PTR) {
ct = ((TypePointer *)ct)->getPtrTo();
if (ct->getMetatype() == TYPE_CODE)
color = EmitMarkup::funcname_color;
}
if (vn0->isConstant()) { // If this is NOT relative to an object reference
pushAtom(Atom(rec->getToken(),vartoken,color,op,outvn));
}
else {
pushOp(&object_member,op);
pushVn(vn0,op,mods);
pushAtom(Atom(rec->getToken(),syntax,color,op,outvn));
}
}
}
}
}
} // End namespace ghidra