Skip to content

Commit

Permalink
fix(smali-input): improve error report for smali assemble (#2411)
Browse files Browse the repository at this point in the history
  • Loading branch information
skylot committed Feb 15, 2025
1 parent 4ef1f3b commit ff66f95
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package jadx.plugins.input.smali;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
Expand Down Expand Up @@ -33,12 +30,8 @@ public boolean execute(List<Path> input, SmaliInputOptions options) {
if (smaliFiles.isEmpty()) {
return false;
}
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
collectSystemErrors(out, () -> compile(smaliFiles, options));
boolean success = out.size() == 0;
if (!success) {
LOG.error("Smali error:\n{}", out);
}
try {
compile(smaliFiles, options);
} catch (Exception e) {
LOG.error("Smali process error", e);
}
Expand All @@ -56,7 +49,7 @@ private void compile(List<Path> inputFiles, SmaliInputOptions options) {
int threads = options.getThreads();
LOG.debug("Compiling smali files: {}, threads: {}", inputFiles.size(), threads);
long start = System.currentTimeMillis();
if (threads == 1) {
if (threads == 1 || inputFiles.size() == 1) {
for (Path inputFile : inputFiles) {
assemble(inputFile, smaliOptions);
}
Expand All @@ -78,12 +71,12 @@ private void compile(List<Path> inputFiles, SmaliInputOptions options) {
}

private void assemble(Path inputFile, SmaliOptions smaliOptions) {
String fileName = inputFile.toAbsolutePath().toString();
try (Reader reader = Files.newBufferedReader(inputFile)) {
byte[] assemble = SmaliUtils.assemble(reader, smaliOptions);
dexData.add(new SimpleDexData(fileName, assemble));
Path path = inputFile.toAbsolutePath();
try {
byte[] dexContent = SmaliUtils.assemble(path.toFile(), smaliOptions);
dexData.add(new SimpleDexData(path.toString(), dexContent));
} catch (Exception e) {
throw new RuntimeException("Fail to compile: " + fileName, e);
LOG.error("Failed to assemble smali file: {}", path, e);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package jadx.plugins.input.smali;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Reader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenSource;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.runtime.tree.TreeNodeStream;

import com.android.tools.smali.dexlib2.Opcodes;
import com.android.tools.smali.dexlib2.writer.builder.DexBuilder;
import com.android.tools.smali.dexlib2.writer.io.MemoryDataStore;
import com.android.tools.smali.smali.LexerErrorInterface;
import com.android.tools.smali.smali.SmaliOptions;
import com.android.tools.smali.smali.smaliFlexLexer;
import com.android.tools.smali.smali.smaliParser;
Expand All @@ -24,31 +27,67 @@
public class SmaliUtils {

@SuppressWarnings("ExtractMethodRecommender")
public static byte[] assemble(Reader reader, SmaliOptions options) throws IOException, RecognitionException {
LexerErrorInterface lexer = new smaliFlexLexer(reader, options.apiLevel);
CommonTokenStream tokens = new CommonTokenStream((TokenSource) lexer);
smaliParser parser = new smaliParser(tokens);
parser.setVerboseErrors(options.verboseErrors);
parser.setAllowOdex(options.allowOdexOpcodes);
parser.setApiLevel(options.apiLevel);
smaliParser.smali_file_return parseResult = parser.smali_file();
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
throw new RuntimeException("Parse error");
public static byte[] assemble(File smaliFile, SmaliOptions options) throws IOException {
StringBuilder errors = new StringBuilder();
try (FileInputStream fis = new FileInputStream(smaliFile);
InputStreamReader reader = new InputStreamReader(fis, StandardCharsets.UTF_8)) {

smaliFlexLexer lexer = new smaliFlexLexer(reader, options.apiLevel);
lexer.setSourceFile(smaliFile);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ParserWrapper parser = new ParserWrapper(tokens, errors);
parser.setVerboseErrors(options.verboseErrors);
parser.setAllowOdex(options.allowOdexOpcodes);
parser.setApiLevel(options.apiLevel);
ParserWrapper.smali_file_return parseResult = parser.smali_file();
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
throw new RuntimeException("Smali parse error: " + errors);
}
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(parseResult.getTree());
treeStream.setTokenStream(tokens);

DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel));
TreeWalkerWrapper dexGen = new TreeWalkerWrapper(treeStream, errors);
dexGen.setApiLevel(options.apiLevel);
dexGen.setVerboseErrors(options.verboseErrors);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();
if (dexGen.getNumberOfSyntaxErrors() > 0) {
throw new RuntimeException("Smali compile error: " + errors);
}
MemoryDataStore dataStore = new MemoryDataStore();
dexBuilder.writeTo(dataStore);
return dataStore.getData();
} catch (RecognitionException e) {
throw new RuntimeException("Smali process error: " + errors, e);
}
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(parseResult.getTree());
treeStream.setTokenStream(tokens);

DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(options.apiLevel));
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.setApiLevel(options.apiLevel);
dexGen.setVerboseErrors(options.verboseErrors);
dexGen.setDexBuilder(dexBuilder);
dexGen.smali_file();
if (dexGen.getNumberOfSyntaxErrors() > 0) {
throw new RuntimeException("Compile error");
}

private static final class ParserWrapper extends smaliParser {
private final StringBuilder errors;

public ParserWrapper(TokenStream input, StringBuilder errors) {
super(input);
this.errors = errors;
}

@Override
public void emitErrorMessage(String msg) {
errors.append('\n').append(msg);
}
}

private static final class TreeWalkerWrapper extends smaliTreeWalker {
private final StringBuilder errors;

public TreeWalkerWrapper(TreeNodeStream input, StringBuilder errors) {
super(input);
this.errors = errors;
}

@Override
public void emitErrorMessage(String msg) {
errors.append('\n').append(msg);
}
MemoryDataStore dataStore = new MemoryDataStore();
dexBuilder.writeTo(dataStore);
return dataStore.getData();
}
}

0 comments on commit ff66f95

Please sign in to comment.