callback) {
@@ -336,14 +338,38 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
return result;
}
+ /**
+ * Uses the given line to find variables (also parameters and return types) and any
+ * accesses to them in that line. A given variable may be used directly or, as in
+ * the case with Composite types, may have one of its fields accessed. Each result
+ * found by this method will be at least a variable access and may also itself have
+ * field accesses.
+ *
+ * Sometimes a line is structured such that there are anonymous variable accesses. This
+ * is the case where a Composite is being accessed, but the Composite itself is
+ * not a variable in the current function. See {@link AnonymousVariableAccessDR} for
+ * more details.
+ *
+ * @param line the current line being processed from the Decompiler
+ * @param results the accumulator into which matches will be placed
+ */
private void findVariablesInLine(ClangLine line, List results) {
+ List allTokens = line.getAllTokens();
+ Iterable filteredTokens = IterableUtils.filteredIterable(allTokens,
+ token -> {
+ // Only include desirable tokens (this is really just for easier debugging).
+ // Update this filter if the loop below ever needs other types of tokens.
+ return (token instanceof ClangTypeToken) ||
+ (token instanceof ClangVariableToken) || (token instanceof ClangFieldToken);
+ });
+
// gather any casts until we can use them (the type they modify will follow)
List castsSoFar = new ArrayList<>();
VariableDR declaration = null;
VariableAccessDR access = null;
- for (ClangToken token : line.getAllTokens()) {
+ for (ClangToken token : filteredTokens) {
if (token instanceof ClangTypeToken) {
@@ -371,16 +397,15 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
//
// Observations:
- // 1) 'variableAccess' will be null if we are on a C statement that
- // is a declaration (parameter or variable). In this case, 'ref' will
- // be an instance of VariableDR.
- // 2) 'variableAccess' will be null the first time a variable is used in
+ // 1) 'access' will be null if we are on a C statement that
+ // is a declaration (parameter or variable). In this case,
+ // 'declaration' will be an instance of VariableDR.
+ // 2) 'access' will be null the first time a variable is used in
// a statement.
- // 3) if 'variableAccess' is non-null, but already has a variable assigned,
+ // 3) if 'access' is non-null, but already has a variable assigned,
// then this means the current ClangVariableToken represents a new
// variable access/usage.
//
-
if (declaration != null) {
declaration.setVariable((ClangVariableToken) token);
declaration = null;
@@ -415,12 +440,32 @@ public class DecompilerDataTypeReferenceFinder implements DataTypeReferenceFinde
}
}
- access.addField((ClangFieldToken) token, casts);
+ ClangFieldToken field = (ClangFieldToken) token;
+ if (typesDoNotMatch(access, field)) {
+ // this can happen when a field is used anonymously, such as directly
+ // after a nested array index operation
+ results.add(new AnonymousVariableAccessDR(line, field));
+ continue;
+ }
+
+ access.addField(field, casts);
castsSoFar.clear();
}
}
}
+ private boolean typesDoNotMatch(VariableAccessDR access, ClangFieldToken field) {
+
+ DecompilerVariable variable = access.getVariable();
+ if (variable == null) {
+ return false; // should not happen
+ }
+
+ DataType variableDt = variable.getDataType();
+ DataType fieldDt = field.getDataType();
+ return !DecompilerReference.isEqual(variableDt, fieldDt);
+ }
+
private VariableAccessDR getLastAccess(List variables) {
// for now, assume that the last access will be the last item we added
if (variables.isEmpty()) {
diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java
index 19a377fd23..d2d511ba1c 100644
--- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java
+++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerReference.java
@@ -83,6 +83,10 @@ public abstract class DecompilerReference {
return var.getAddress();
}
+ public ClangLine getLine() {
+ return line;
+ }
+
protected String getContext() {
String context = getContext(variable);
return context;
diff --git a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java
index df55a3cd3b..e20b59157d 100644
--- a/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java
+++ b/Ghidra/Features/DecompilerDependent/src/main/java/ghidra/app/extension/datatype/finder/DecompilerVariable.java
@@ -52,7 +52,7 @@ public abstract class DecompilerVariable {
}
// Note: this is the icky part of the API. How to know from where to get the data type?
- HighVariable highVariable = variable.getHighVariable();
+ HighVariable highVariable = variable.getHighVariable();
if (highVariable != null) {
return highVariable.getDataType();
}
@@ -62,7 +62,7 @@ public abstract class DecompilerVariable {
if (dataType != null) {
return dataType;
}
-
+
// Prefer the type of the first input varnode, unless that type is a 'void *'.
// Usually, in that special case, the output varnode has the correct type information.
PcodeOp op = variable.getPcodeOp();
@@ -104,7 +104,19 @@ public abstract class DecompilerVariable {
}
Varnode output = op.getOutput();
- return output.getHigh().getDataType();
+ if (output == null) {
+ // can happen when a variable in volatile memory is used in a write_volatile
+ // pseudo operation
+ return null;
+ }
+
+ HighVariable high = output.getHigh();
+ if (high == null) {
+ // not sure if this can happen; just in case
+ return null;
+ }
+
+ return high.getDataType();
}
private DataType getDataType(Varnode varnode) {
diff --git a/Ghidra/Framework/Generic/build.gradle b/Ghidra/Framework/Generic/build.gradle
index a9b1d6a613..0fd371c61b 100644
--- a/Ghidra/Framework/Generic/build.gradle
+++ b/Ghidra/Framework/Generic/build.gradle
@@ -8,8 +8,8 @@ dependencies {
compile "cglib:cglib-nodep:2.2"
compile "com.google.guava:guava:19.0"
compile "org.jdom:jdom-legacy:1.1.3"
- compile "org.apache.logging.log4j:log4j-api:2.8.1"
- compile "org.apache.logging.log4j:log4j-core:2.8.1"
+ compile "org.apache.logging.log4j:log4j-api:2.8.2"
+ compile "org.apache.logging.log4j:log4j-core:2.8.2"
compile "org.apache.commons:commons-collections4:4.1"
compile "org.apache.commons:commons-lang3:3.5"
compile "org.lucee:commons-io:2.6.0"
diff --git a/Ghidra/Framework/Generic/certification.manifest b/Ghidra/Framework/Generic/certification.manifest
index 348cdd700a..f6b8cb7877 100644
--- a/Ghidra/Framework/Generic/certification.manifest
+++ b/Ghidra/Framework/Generic/certification.manifest
@@ -2,13 +2,13 @@
##MODULE IP: Apache License 2.0
##MODULE IP: BSD
##MODULE IP: Christian Plattner
+##MODULE IP: Crystal Clear Icons - LGPL 2.1
##MODULE IP: JDOM License
##MODULE IP: MIT
-##MODULE IP: Nuvola Icons - LGPL 2.1
##MODULE IP: Modified Nuvola Icons - LGPL 2.1
-##MODULE IP: Tango Icons - Public Domain
+##MODULE IP: Nuvola Icons - LGPL 2.1
##MODULE IP: Oxygen Icons - LGPL 3.0
-##MODULE IP: Crystal Clear Icons - LGPL 2.1
+##MODULE IP: Tango Icons - Public Domain
.classpath||GHIDRA||||END|
.gitignore||GHIDRA||||END|
.project||GHIDRA||||END|
diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
index 8a8bd055f2..4274fa637e 100644
--- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
+++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/PcodeDataTypeManager.java
@@ -261,7 +261,16 @@ public class PcodeDataTypeManager {
if (type instanceof Array) {
return buildType(type, size);
}
- if (!(type instanceof FunctionDefinition) && type.getLength() <= 0) {
+ if (type instanceof FunctionDefinition) {
+ long id = progDataTypes.getID(type);
+ if (id <= 0) {
+ // Its possible the FunctionDefinition was built on the fly and is not
+ // a permanent data-type of the program with an ID. In this case, we can't
+ // construct a tag but must build a full tag.
+ return buildType(type, size);
+ }
+ }
+ else if (type.getLength() <= 0) {
return buildType(type, size);
}
StringBuilder resBuf = new StringBuilder();
diff --git a/Ghidra/Processors/6502/data/languages/6502.slaspec b/Ghidra/Processors/6502/data/languages/6502.slaspec
index 0f1833aeac..71394f02ec 100644
--- a/Ghidra/Processors/6502/data/languages/6502.slaspec
+++ b/Ghidra/Processors/6502/data/languages/6502.slaspec
@@ -78,35 +78,46 @@ define pcodeop readIRQ;
################################################################
REL: reloc is rel [ reloc = inst_next + rel; ] { export *:2 reloc; }
+# Immediate
+OP1: "#"imm8 is bbb=2; imm8 { tmp:1 = imm8; export tmp; }
+# Zero Page
+OP1: imm8 is bbb=1; imm8 { export *:1 imm8; }
+# Zero Page Indexed X
+OP1: imm8,X is bbb=5 & X; imm8 { tmp:2 = zext(imm8 + X); export *:1 tmp; }
+# Absolute
+OP1: imm16 is bbb=3; imm16 { export *:1 imm16; }
+# Absolute Indexed X
+OP1: imm16,X is bbb=7 & X; imm16 { tmp:2 = imm16 + zext(X); export *:1 tmp; }
+# Absolute Indexed Y
+OP1: imm16,Y is bbb=6 & Y; imm16 { tmp:2 = imm16 + zext(Y); export *:1 tmp; }
+# Indirect X
+OP1: (imm8,X) is bbb=0 & X; imm8 { addr:2 = zext(imm8 + X); tmp:2 = *:2 addr; export *:1 tmp; }
+# Indirect Y
+OP1: (imm8),Y is bbb=4 & Y; imm8 { addr:2 = imm8; tmp:2 = *:2 addr; tmp = tmp + zext(Y); export *:1 tmp; }
-OP1: (imm8,X) is bbb=0 & X; imm8 { addr:2 = imm8 + zext(X); tmp:2 = *:2 addr; export *:1 tmp; }
-OP1: imm8 is bbb=1; imm8 { export *:1 imm8; }
-OP1: "#"imm8 is bbb=2; imm8 { tmp:1 = imm8; export tmp; }
-OP1: imm16 is bbb=3; imm16 { export *:1 imm16; }
-OP1: (imm8),Y is bbb=4 & Y; imm8 { addr:2 = imm8; tmp:2 = *:2 addr; tmp = tmp + zext(Y); export *:1 tmp; }
-OP1: imm8,X is bbb=5 & X; imm8 { tmp:2 = imm8 + zext(X); export *:1 tmp; }
-OP1: imm16,Y is bbb=6 & Y; imm16 { tmp:2 = imm16 + zext(Y); export *:1 tmp; }
-OP1: imm16,X is bbb=7 & X; imm16 { tmp:2 = imm16 + zext(X); export *:1 tmp; }
+# Immediate
+OP2: "#"imm8 is bbb=0; imm8 { tmp:1 = imm8; export tmp; }
+# Zero Page
+OP2: imm8 is bbb=1; imm8 { export *:1 imm8; }
+OP2: A is bbb=2 & A { export A; }
+# Absolute
+OP2: imm16 is bbb=3; imm16 { export *:1 imm16; }
+# Zero Page Indexed X
+OP2: imm8,X is bbb=5 & X; imm8 { tmp:2 = zext(imm8 + X); export *:1 tmp; }
+# Absolute Indexed X
+OP2: imm16,X is bbb=7 & X; imm16 { tmp:2 = imm16 + zext(X); export *:1 tmp; }
+
+OP2ST: OP2 is OP2 { export OP2; }
+OP2ST: imm8,Y is bbb=5 & Y; imm8 { tmp:2 = zext(imm8 + Y); export *:1 tmp; }
+
+OP2LD: OP2 is OP2 { export OP2; }
+OP2LD: imm8,Y is bbb=5 & Y; imm8 { tmp:2 = zext(imm8 + Y); export *:1 tmp; }
+OP2LD: imm16,Y is bbb=7 & Y; imm16 { tmp:2 = imm16 + zext(Y); export *:1 tmp; }
-OP2: "#"imm8 is bbb=0; imm8 { tmp:1 = imm8; export tmp; }
-OP2: imm8 is bbb=1; imm8 { export *:1 imm8; }
-OP2: A is bbb=2 & A { export A; }
-OP2: imm16 is bbb=3; imm16 { export *:1 imm16; }
-OP2: imm8,X is bbb=5 & X; imm8 { tmp:2 = imm8 + zext(X); export *:1 tmp; }
-OP2: imm16,X is bbb=7 & X; imm16 { tmp:2 = imm16 + zext(X); export *:1 tmp; }
-
-OP2ST: OP2 is OP2 { export OP2; }
-OP2ST: imm8,Y is bbb=5 & Y; imm8 { tmp:2 = imm8 + zext(Y); export *:1 tmp; }
-
-OP2LD: OP2 is OP2 { export OP2; }
-OP2LD: imm8,Y is bbb=5 & Y; imm8 { tmp:2 = imm8 + zext(Y); export *:1 tmp; }
-OP2LD: imm16,Y is bbb=7 & Y; imm16 { tmp:2 = imm16 + zext(Y); export *:1 tmp; }
-
-
-ADDR8: imm8 is imm8 { export *:1 imm8; }
-ADDR16: imm16 is imm16 { export *:1 imm16; }
-ADDRI: imm16 is imm16 { tmp:2 = imm16; export *:2 tmp; }
+ADDR8: imm8 is imm8 { export *:1 imm8; }
+ADDR16: imm16 is imm16 { export *:1 imm16; }
+ADDRI: imm16 is imm16 { tmp:2 = imm16; export *:2 tmp; }
# Instructions
diff --git a/Ghidra/Processors/68000/data/languages/68000.sinc b/Ghidra/Processors/68000/data/languages/68000.sinc
index aab63359fd..92d8346636 100644
--- a/Ghidra/Processors/68000/data/languages/68000.sinc
+++ b/Ghidra/Processors/68000/data/languages/68000.sinc
@@ -306,6 +306,8 @@ define pcodeop saveFPUStateFrame;
define pcodeop restoreFPUStateFrame;
define pcodeop pushInvalidateCaches;
+define pcodeop bcdAdjust;
+
define pcodeop sin;
define pcodeop cos;
define pcodeop tan;
@@ -638,12 +640,15 @@ macro clearflags_fp() {
$(NAN_FP) = 0;
}
-# SCR 10997:
+macro bcdflags(result) {
+ XF = CF;
+ ZF = (result == 0) * ZF + (result != 0);
+}
+
macro getbit(res,in,bitnum) {
res = ((in >> bitnum) & 1) != 0;
}
-# SCR 10997:
macro bitmask(res, width) {
res = (1 << width) - 1;
}
@@ -656,7 +661,6 @@ macro getbitfield(res, off, width) {
res = (res << off) >> (32 - width);
}
-# SCR 10997:
macro resbitflags(result, bitnum) {
NF = ((result >> bitnum) & 1) != 0;
ZF = result == 0;
@@ -687,7 +691,7 @@ macro extendedResultFlags(result) {
with : extGUARD=1 {
-:abcd Tyb,Txb is op=12 & op48=16 & Tyb & Txb unimpl
+:abcd Tyb,Txb is op=12 & op48=16 & Tyb & Txb { CF = carry(Tyb,carry(Txb,XF)); Tyb = Tyb + Txb + XF; Tyb = bcdAdjust(Tyb); bcdflags(Tyb); }
:add.b eab,reg9dnb is (op=13 & reg9dnb & op68=0)... & eab { addflags(eab,reg9dnb); reg9dnb = reg9dnb + eab; resflags(reg9dnb); }
:add.w eaw,reg9dnw is (op=13 & reg9dnw & op68=1)... & eaw { addflags(eaw,reg9dnw); reg9dnw = reg9dnw + eaw; resflags(reg9dnw); }
@@ -1391,7 +1395,8 @@ submul: regdr-regdq is regdq & divsgn=0 & divsz=1 & regdr { tmp1:8 = zext(glb
regdr=res(4); resflags(res); CF=0; VF=0; }
:mul^mulsize e2l,submul is opbig=0x4c & op67=0 & $(DAT_ALTER_ADDR_MODES); submul & mulsize; e2l [ savmod2=savmod1; regtsan=regtfan; ] { glbdenom=e2l; build submul; }
-:nbcd eab is (opbig=0x48 & op67=0 & $(DAT_ALTER_ADDR_MODES))... & eab unimpl
+:nbcd eab is (opbig=0x48 & op67=0 & $(DAT_ALTER_ADDR_MODES))... & eab
+ { tmp:1 = eab; CF = (tmp != 0) || (XF == 1); tmp = 0 - tmp - XF; eab = bcdAdjust(tmp); bcdflags(tmp); }
# NB: For the neg insn the CF carry flag is not set like other insns, from the manual:
@@ -1600,7 +1605,8 @@ ptestLevel: "#"^mregn is mregn { export *[const]:1 mregn; }
:rts is opbig=0x4e & op37=14 & op02=5 { PC = *SP; SP = SP+4; return [PC]; }
-:sbcd Tyb,Txb is op=8 & op48=16 & Txb & Tyb unimpl
+:sbcd Tyb,Txb is op=8 & op48=16 & Txb & Tyb
+ { CF = (Tyb < Txb) || ( (XF == 1) && (Tyb == Txb) ); Tyb = Tyb - Txb - XF; Tyb = bcdAdjust(Tyb); bcdflags(Tyb); }
:s^cc eab is (op=5 & cc & op67=3 & $(DAT_ALTER_ADDR_MODES))... & eab { eab = -cc; }
diff --git a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java
index cb64e5bac2..f0e3211c86 100644
--- a/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java
+++ b/Ghidra/Processors/Dalvik/src/main/java/ghidra/dalvik/dex/inject/ConstantPoolDex.java
@@ -56,8 +56,9 @@ public class ConstantPoolDex extends ConstantPool {
String classString =
DexUtil.convertTypeIndexToString(dexHeader, fieldIDItem.getClassIndex());
String[] pathArray = DexUtil.convertClassStringToPathArray("", classString);
- if (pathArray != null)
+ if (pathArray != null) {
res.token = pathArray[pathArray.length - 1] + '.' + res.token;
+ }
}
DataType fieldDT = dexHeader.getDataType(program, fieldIDItem.getTypeIndex());
@@ -72,13 +73,16 @@ public class ConstantPoolDex extends ConstantPool {
private String removeUniquifier(String name) {
int len = name.length();
- if (len < 10 || name.charAt(len - 9) != '_')
+ if (len < 10 || name.charAt(len - 9) != '_') {
return name;
+ }
char matchChar = name.charAt(len - 8);
- if (matchChar != '5' && matchChar != 'e')
+ if (matchChar != '5' && matchChar != 'e') {
return name;
- if (name.charAt(len - 7) != '0')
+ }
+ if (name.charAt(len - 7) != '0') {
return name;
+ }
return name.substring(0, len - 9);
}
@@ -103,14 +107,19 @@ public class ConstantPoolDex extends ConstantPool {
String classString =
DexUtil.convertTypeIndexToString(dexHeader, methodIDItem.getClassIndex());
String[] pathArray = DexUtil.convertClassStringToPathArray("", classString);
- if (pathArray != null)
+ if (pathArray != null) {
namespaceString = pathArray[pathArray.length - 1];
+ }
}
- if (namespaceString != null)
+ if (namespaceString != null) {
res.token = namespaceString + '.' + res.token;
+ }
}
res.tag = ConstantPool.POINTER_METHOD;
- FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(res.token, dtManager);
+ // The FunctionDefinition is constructed on the fly, essentially as an anonymous type
+ // We use an internal naming scheme involding the the methodID to avoid name collisions
+ String defName = res.token + '_' + Integer.toHexString(methodID);
+ FunctionDefinitionDataType funcDef = new FunctionDefinitionDataType(defName, dtManager);
res.type = new PointerDataType(funcDef);
res.hasThisPtr = !isStatic;
diff --git a/GhidraBuild/BuildFiles/JsonDoclet/build.gradle b/GhidraBuild/BuildFiles/JsonDoclet/build.gradle
index f9b749d998..db5fa4daf4 100644
--- a/GhidraBuild/BuildFiles/JsonDoclet/build.gradle
+++ b/GhidraBuild/BuildFiles/JsonDoclet/build.gradle
@@ -4,5 +4,7 @@ eclipse.project.name = '_JsonDoclet'
apply plugin: 'java'
dependencies {
- compile 'com.googlecode.json-simple:json-simple:1.1.1'
+ compile('com.googlecode.json-simple:json-simple:1.1.1') {
+ exclude group: 'junit', module: 'junit'
+ }
}