Skip to content

Commit

Permalink
fix(gui): make bytecode output closer to smali (#1739)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Dec 25, 2022
1 parent 5d186e5 commit df38a64
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1286,7 +1286,10 @@ public int getRegNum() {
@Override
public String getType() {
String gen = getSignature();
return gen.isEmpty() ? this.slot.signature : gen;
if (gen == null || gen.isEmpty()) {
return this.slot.signature;
}
return gen;
}

@NonNull
Expand All @@ -1304,6 +1307,11 @@ public int getStartOffset() {
public int getEndOffset() {
return (int) (slot.codeIndex + slot.length);
}

@Override
public boolean isMarkedAsParameter() {
return false;
}
}

public static class RuntimeDebugInfo {
Expand Down
108 changes: 58 additions & 50 deletions jadx-gui/src/main/java/jadx/gui/device/debugger/smali/Smali.java
Original file line number Diff line number Diff line change
Expand Up @@ -285,24 +285,25 @@ private void writeMethod(SmaliWriter smali, MethodNode methodNode, IMethodData m
writeMethodDef(smali, mth, line);
ICodeReader codeReader = mth.getCodeReader();
if (codeReader != null) {
int regsCount = codeReader.getRegistersCount();
line.smaliMthNode.setParamRegStart(getParamStartRegNum(mth));
line.smaliMthNode.setRegCount(codeReader.getRegistersCount());
line.smaliMthNode.setRegCount(regsCount);
Map<Long, InsnNode> nodes = new HashMap<>(codeReader.getUnitsCount() / 2);
line.smaliMthNode.setInsnNodes(nodes, codeReader.getUnitsCount());
line.smaliMthNode.initRegInfoList(codeReader.getRegistersCount(), codeReader.getUnitsCount());
line.smaliMthNode.initRegInfoList(regsCount, codeReader.getUnitsCount());

smali.incIndent();
smali.startLine(".registers ")
.add("" + codeReader.getRegistersCount())
.startLine();
smali.startLine(".registers ").add(Integer.toString(regsCount));

writeTries(codeReader, line);
if (formatMthParamInfo(mth, smali, codeReader, line)) {
smali.startLine();
IDebugInfo debugInfo = codeReader.getDebugInfo();
List<ILocalVar> localVars = debugInfo != null ? debugInfo.getLocalVars() : Collections.emptyList();
formatMthParamInfo(mth, smali, line, regsCount, localVars);
if (debugInfo != null) {
formatDbgInfo(debugInfo, localVars, line);
}
smali.newLine();
smali.startLine();
if (codeReader.getDebugInfo() != null) {
formatDbgInfo(codeReader.getDebugInfo(), line);
}
// first pass to fill payload offsets for switch instructions
codeReader.visitInstructions(insn -> {
Opcode opcode = insn.getOpcode();
Expand Down Expand Up @@ -458,20 +459,11 @@ private void writeMethodDef(SmaliWriter smali, IMethodData mth, LineInfo lineInf
}
}

private boolean formatMthParamInfo(IMethodData mth, SmaliWriter smali, ICodeReader codeReader, LineInfo line) {
private void formatMthParamInfo(IMethodData mth, SmaliWriter smali, LineInfo line,
int regsCount, List<ILocalVar> localVars) {
List<String> types = mth.getMethodRef().getArgTypes();
if (types.isEmpty()) {
return false;
}
ILocalVar[] params = new ILocalVar[codeReader.getRegistersCount()];
IDebugInfo dbgInfo = codeReader.getDebugInfo();
if (dbgInfo != null) {
for (ILocalVar var : dbgInfo.getLocalVars()) {
// collect only method parameters
if (var.getStartOffset() <= 0) {
params[var.getRegNum()] = var;
}
}
return;
}
int paramStart = 0;
int regNum = line.smaliMthNode.getParamRegStart();
Expand All @@ -482,26 +474,30 @@ private boolean formatMthParamInfo(IMethodData mth, SmaliWriter smali, ICodeRead
regNum++;
paramStart++;
}
if (localVars.isEmpty()) {
return;
}
ILocalVar[] params = new ILocalVar[regsCount];
for (ILocalVar var : localVars) {
if (var.isMarkedAsParameter()) {
params[var.getRegNum()] = var;
}
}
smali.newLine();
for (String paramType : types) {
String name;
String type;
ILocalVar param = params[regNum];
if (param != null) {
name = Utils.getOrElse(param.getName(), "");
type = Utils.getOrElse(param.getSignature(), paramType);
} else {
name = "";
type = paramType;
String name = Utils.getOrElse(param.getName(), "");
String type = Utils.getOrElse(param.getSignature(), paramType);
String varName = "p" + paramStart;
smali.startLine(String.format(".param %s, \"%s\" # %s", varName, name, type));
line.addRegName(regNum, varName);
line.smaliMthNode.setParamReg(regNum, varName);
}
String varName = "p" + paramStart;
smali.startLine(String.format(".param %s, \"%s\" # %s", varName, name, type));
line.addRegName(regNum, varName);
line.smaliMthNode.setParamReg(regNum, varName);
int regSize = isWideType(paramType) ? 2 : 1;
regNum += regSize;
paramStart += regSize;
}
return true;
}

private static int getParamStartRegNum(IMethodData mth) {
Expand Down Expand Up @@ -558,30 +554,42 @@ private void writeAnnotation(SmaliWriter smali, IAnnotation anno) {
smali.startLine(".end annotation");
}

private void formatDbgInfo(IDebugInfo dbgInfo, LineInfo line) {
private void formatDbgInfo(IDebugInfo dbgInfo, List<ILocalVar> localVars, LineInfo line) {
dbgInfo.getSourceLineMapping().forEach((codeOffset, srcLine) -> {
if (codeOffset > -1) {
line.addDebugLineTip(codeOffset, String.format(".line %d", srcLine), "");
}
});
for (ILocalVar localVar : dbgInfo.getLocalVars()) {
String type = localVar.getSignature();
if (type == null || type.trim().isEmpty()) {
type = localVar.getType();
}
if (localVar.getStartOffset() > -1) {
line.addTip(
localVar.getStartOffset(),
String.format(".local v%d", localVar.getRegNum()),
String.format(", \"%s\":%s", localVar.getName(), type));
for (ILocalVar localVar : localVars) {
if (localVar.isMarkedAsParameter()) {
continue;
}
if (localVar.getEndOffset() > -1) {
line.addTip(
localVar.getEndOffset(),
String.format(".end local v%d", localVar.getRegNum()),
String.format(" # \"%s\":%s", localVar.getName(), type));
String type = localVar.getType();
String sign = localVar.getSignature();
String longTypeStr;
if (sign == null || sign.trim().isEmpty()) {
longTypeStr = String.format(", \"%s\":%s", localVar.getName(), type);
} else {
longTypeStr = String.format(", \"%s\":%s, \"%s\"", localVar.getName(), type, localVar.getSignature());
}
line.addTip(
localVar.getStartOffset(),
".local " + formatVarName(line.smaliMthNode, localVar),
longTypeStr);
line.addTip(
localVar.getEndOffset(),
".end local " + formatVarName(line.smaliMthNode, localVar),
String.format(" # \"%s\":%s", localVar.getName(), type));
}
}

private String formatVarName(SmaliMethodNode smaliMthNode, ILocalVar localVar) {
int paramRegStart = smaliMthNode.getParamRegStart();
int regNum = localVar.getRegNum();
if (regNum < paramRegStart) {
return "v" + regNum;
}
return "p" + (regNum - paramRegStart);
}

private void writeEncodedValue(SmaliWriter smali, EncodedValue value, boolean wrapArray) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ protected void setDefPos(int pos) {
protected void setParamReg(int regNum, String name) {
SmaliRegister r = regList.get(regNum);
r.setParam(name);
r.setStartOffset(-1);
}

protected void setParamRegStart(int paramRegStart) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ protected void setParam(String name) {
}

protected void setStartOffset(int off) {
if (startOffset == -1 && !isParam) {
startOffset = off;
return;
}
if (off < startOffset) {
startOffset = off;
}
Expand Down Expand Up @@ -71,4 +67,9 @@ public int getStartOffset() {
public int getEndOffset() {
return endOffset;
}

@Override
public boolean isMarkedAsParameter() {
return isParam;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ void testParams() {
Smali disasm = Smali.disassemble(cls);
String code = disasm.getCode();
LOG.debug("{}", code);
assertThat(code).doesNotContain("Failed to write method");
assertThat(code)
.doesNotContain("Failed to write method")
.doesNotContain(".param p1")
.contains(".local p1, \"arg0\":Landroid/widget/AdapterView;, \"Landroid/widget/AdapterView<*>;\"");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,9 @@ public DebugInfo process(int debugOff) {
int nameId = in.readUleb128p1();
String name = ext.getString(nameId);
if (name != null && i < argsCount) {
int regNum = argRegs[i];
startVar(new DexLocalVar(regNum, name, argTypes.get(i)), -1);
DexLocalVar paramVar = new DexLocalVar(argRegs[i], name, argTypes.get(i));
startVar(paramVar, addr);
paramVar.markAsParameter();
varsInfoFound = true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import jadx.plugins.input.dex.sections.SectionReader;

public class DexLocalVar implements ILocalVar {
private static final int PARAM_START_OFFSET = -1;

private final int regNum;
private final String name;
private final String type;
Expand Down Expand Up @@ -78,6 +80,15 @@ public int getStartOffset() {
return startOffset;
}

public void markAsParameter() {
startOffset = PARAM_START_OFFSET;
}

@Override
public boolean isMarkedAsParameter() {
return startOffset == PARAM_START_OFFSET;
}

@Override
public int getEndOffset() {
return endOffset;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public int getEndOffset() {
return endOffset;
}

@Override
public boolean isMarkedAsParameter() {
return false;
}

@Override
public int hashCode() {
int result = regNum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ public interface ILocalVar {
int getStartOffset();

int getEndOffset();

/**
* Hint if variable is a method parameter.
* Can be incorrect and shouldn't be trusted.
*/
boolean isMarkedAsParameter();
}

0 comments on commit df38a64

Please sign in to comment.