mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: add option to strip off information about call site locations.
This decreases executable size significantly. However, this produces obfuscated stack traces which can be deobfuscated using JSON symbol table.
This commit is contained in:
parent
c78874f426
commit
3b4cc43e79
|
@ -111,10 +111,7 @@ public class TThrowable extends RuntimeException {
|
|||
|
||||
@Unmanaged
|
||||
private static TStackTraceElement[] fillInStackTraceLowLevel() {
|
||||
int stackSize = ExceptionHandling.callStackSize();
|
||||
TStackTraceElement[] stackTrace = new TStackTraceElement[stackSize];
|
||||
ExceptionHandling.fillStackTrace((StackTraceElement[]) (Object) stackTrace);
|
||||
return stackTrace;
|
||||
return (TStackTraceElement[]) (Object) ExceptionHandling.fillStackTrace();
|
||||
}
|
||||
|
||||
@Rename("getMessage")
|
||||
|
|
|
@ -18,6 +18,10 @@ package org.teavm.backend.c;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -78,6 +82,7 @@ import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
|
|||
import org.teavm.backend.lowlevel.transform.WeakReferenceTransformation;
|
||||
import org.teavm.cache.EmptyMethodNodeCache;
|
||||
import org.teavm.cache.MethodNodeCache;
|
||||
import org.teavm.common.JsonUtil;
|
||||
import org.teavm.dependency.ClassDependency;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
|
@ -169,6 +174,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
private SimpleStringPool stringPool;
|
||||
private boolean longjmpUsed = true;
|
||||
private boolean heapDump;
|
||||
private boolean obfuscated;
|
||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||
|
||||
public CTarget(NameProvider nameProvider) {
|
||||
|
@ -203,6 +209,10 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
this.astCache = astCache;
|
||||
}
|
||||
|
||||
public void setObfuscated(boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClassHolderTransformer> getTransformers() {
|
||||
List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
|
@ -392,7 +402,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
controller.getDependencyInfo(), stringPool, nameProvider, controller.getDiagnostics(), classes,
|
||||
intrinsics, generators, asyncMethods::contains, buildTarget,
|
||||
controller.getClassInitializerInfo(), incremental, longjmpUsed,
|
||||
vmAssertions, vmAssertions || heapDump);
|
||||
vmAssertions, vmAssertions || heapDump, obfuscated);
|
||||
|
||||
BufferedCodeWriter specialWriter = new BufferedCodeWriter(false);
|
||||
BufferedCodeWriter configHeaderWriter = new BufferedCodeWriter(false);
|
||||
|
@ -410,6 +420,9 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
if (heapDump) {
|
||||
configHeaderWriter.println("#define TEAVM_HEAP_DUMP 1");
|
||||
}
|
||||
if (obfuscated) {
|
||||
configHeaderWriter.println("#define TEAVM_OBFUSCATED 1");
|
||||
}
|
||||
|
||||
ClassGenerator classGenerator = new ClassGenerator(context, tagRegistry, decompiler,
|
||||
controller.getCacheStatus());
|
||||
|
@ -522,6 +535,61 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
|||
? this.callSites
|
||||
: CallSiteDescriptor.extract(context.getClassSource(), classNames);
|
||||
new CallSiteGenerator(context, writer, includes, "teavm_callSites").generate(callSites);
|
||||
if (obfuscated) {
|
||||
generateCallSitesJson(context.getBuildTarget(), callSites);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateCallSitesJson(BuildTarget buildTarget, List<? extends CallSiteDescriptor> callSites) {
|
||||
try (OutputStream output = buildTarget.createResource("callsites.json");
|
||||
Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
||||
writer.append("[\n");
|
||||
boolean first = true;
|
||||
for (CallSiteDescriptor descriptor : callSites) {
|
||||
if (!first) {
|
||||
writer.append(",\n");
|
||||
}
|
||||
first = false;
|
||||
writer.append("{\"id\":").append(Integer.toString(descriptor.getId()));
|
||||
writer.append(",\"locations\":[");
|
||||
org.teavm.model.lowlevel.CallSiteLocation[] locations = descriptor.getLocations();
|
||||
if (locations != null) {
|
||||
boolean firstLocation = true;
|
||||
for (org.teavm.model.lowlevel.CallSiteLocation location : locations) {
|
||||
if (!firstLocation) {
|
||||
writer.append(",");
|
||||
}
|
||||
firstLocation = false;
|
||||
writer.append("{\"class\":");
|
||||
appendJsonString(writer, location.getClassName());
|
||||
writer.append(",\"method\":");
|
||||
appendJsonString(writer, location.getMethodName());
|
||||
writer.append(",\"file\":");
|
||||
appendJsonString(writer, location.getFileName());
|
||||
writer.append(",\"line\":").append(Integer.toString(location.getLineNumber()));
|
||||
writer.append("}");
|
||||
}
|
||||
}
|
||||
writer.append("]}");
|
||||
}
|
||||
if (!first) {
|
||||
writer.append("\n");
|
||||
}
|
||||
writer.append("]");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendJsonString(Writer writer, String string) throws IOException {
|
||||
if (string == null) {
|
||||
writer.append("null");
|
||||
return;
|
||||
}
|
||||
|
||||
writer.append("\"");
|
||||
JsonUtil.writeEscapedString(writer, string);
|
||||
writer.append("\"");
|
||||
}
|
||||
|
||||
private void generateStrings(BuildTarget buildTarget, GenerationContext context) throws IOException {
|
||||
|
|
|
@ -26,7 +26,6 @@ import org.teavm.model.lowlevel.CallSiteLocation;
|
|||
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||
|
||||
public class CallSiteGenerator {
|
||||
|
||||
private GenerationContext context;
|
||||
private CodeWriter writer;
|
||||
private IncludeManager includes;
|
||||
|
@ -79,20 +78,22 @@ public class CallSiteGenerator {
|
|||
first = false;
|
||||
|
||||
int locationIndex = -1;
|
||||
CallSiteLocation[] locations = callSite.getLocations();
|
||||
if (locations != null && locations.length > 0) {
|
||||
LocationList prevList = null;
|
||||
for (int i = locations.length - 1; i >= 0; --i) {
|
||||
LocationList list = new LocationList(locations[i], prevList);
|
||||
locationIndex = locationMap.getOrDefault(list, -1);
|
||||
if (locationIndex < 0) {
|
||||
locationIndex = this.locations.size();
|
||||
locationMap.put(list, locationIndex);
|
||||
this.locations.add(list);
|
||||
} else {
|
||||
list = this.locations.get(locationIndex);
|
||||
if (!context.isObfuscated()) {
|
||||
CallSiteLocation[] locations = callSite.getLocations();
|
||||
if (locations != null && locations.length > 0) {
|
||||
LocationList prevList = null;
|
||||
for (int i = locations.length - 1; i >= 0; --i) {
|
||||
LocationList list = new LocationList(locations[i], prevList);
|
||||
locationIndex = locationMap.getOrDefault(list, -1);
|
||||
if (locationIndex < 0) {
|
||||
locationIndex = this.locations.size();
|
||||
locationMap.put(list, locationIndex);
|
||||
this.locations.add(list);
|
||||
} else {
|
||||
list = this.locations.get(locationIndex);
|
||||
}
|
||||
prevList = list;
|
||||
}
|
||||
prevList = list;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,13 +50,14 @@ public class GenerationContext {
|
|||
private boolean longjmp;
|
||||
private boolean vmAssertions;
|
||||
private boolean heapDump;
|
||||
private boolean obfuscated;
|
||||
|
||||
public GenerationContext(VirtualTableProvider virtualTableProvider, Characteristics characteristics,
|
||||
DependencyInfo dependencies, StringPool stringPool, NameProvider names, Diagnostics diagnostics,
|
||||
ClassReaderSource classSource, List<Intrinsic> intrinsics, List<Generator> generators,
|
||||
Predicate<MethodReference> asyncMethods, BuildTarget buildTarget,
|
||||
ClassInitializerInfo classInitializerInfo, boolean incremental, boolean longjmp, boolean vmAssertions,
|
||||
boolean heapDump) {
|
||||
boolean heapDump, boolean obfuscated) {
|
||||
this.virtualTableProvider = virtualTableProvider;
|
||||
this.characteristics = characteristics;
|
||||
this.dependencies = dependencies;
|
||||
|
@ -73,6 +74,7 @@ public class GenerationContext {
|
|||
this.longjmp = longjmp;
|
||||
this.vmAssertions = vmAssertions;
|
||||
this.heapDump = heapDump;
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
public void addIntrinsic(Intrinsic intrinsic) {
|
||||
|
@ -152,4 +154,8 @@ public class GenerationContext {
|
|||
public boolean isVmAssertions() {
|
||||
return vmAssertions;
|
||||
}
|
||||
|
||||
public boolean isObfuscated() {
|
||||
return obfuscated;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
case "isJumpSupported":
|
||||
case "jumpToFrame":
|
||||
case "abort":
|
||||
case "isObfuscated":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
@ -64,6 +65,10 @@ public class ExceptionHandlingIntrinsic implements Intrinsic {
|
|||
context.includes().addInclude("<stdlib.h>");
|
||||
context.writer().print("abort();");
|
||||
break;
|
||||
|
||||
case "isObfuscated":
|
||||
context.writer().print("TEAVM_OBFUSCATED");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
|||
case "isJumpSupported":
|
||||
case "jumpToFrame":
|
||||
case "abort":
|
||||
case "isObfuscated":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -83,6 +84,7 @@ public class ExceptionHandlingIntrinsic implements WasmIntrinsic {
|
|||
}
|
||||
|
||||
case "isJumpSupported":
|
||||
case "isObfuscated":
|
||||
return new WasmInt32Constant(0);
|
||||
|
||||
case "jumpToFrame":
|
||||
|
|
53
core/src/main/java/org/teavm/common/JsonUtil.java
Normal file
53
core/src/main/java/org/teavm/common/JsonUtil.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2019 konsoletyper.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.teavm.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public final class JsonUtil {
|
||||
private JsonUtil() {
|
||||
}
|
||||
|
||||
public static void writeEscapedString(Writer output, String str) throws IOException {
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
char c = str.charAt(i);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
output.write("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
output.write("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
output.write("\\t");
|
||||
break;
|
||||
case '\b':
|
||||
output.write("\\b");
|
||||
break;
|
||||
case '\\':
|
||||
output.write("\\\\");
|
||||
break;
|
||||
case '"':
|
||||
output.write("\\\"");
|
||||
break;
|
||||
default:
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ package org.teavm.debugging.information;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import org.teavm.common.JsonUtil;
|
||||
|
||||
class SourceMapsWriter {
|
||||
private static final String BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
@ -34,10 +35,10 @@ class SourceMapsWriter {
|
|||
public void write(String generatedFile, String sourceRoot, DebugInformation debugInfo) throws IOException {
|
||||
output.write("{\"version\":3");
|
||||
output.write(",\"file\":\"");
|
||||
writeEscapedString(generatedFile);
|
||||
JsonUtil.writeEscapedString(output, generatedFile);
|
||||
output.write("\"");
|
||||
output.write(",\"sourceRoot\":\"");
|
||||
writeEscapedString(sourceRoot);
|
||||
JsonUtil.writeEscapedString(output, sourceRoot);
|
||||
output.write("\"");
|
||||
output.write(",\"sources\":[");
|
||||
for (int i = 0; i < debugInfo.fileNames.length; ++i) {
|
||||
|
@ -45,7 +46,7 @@ class SourceMapsWriter {
|
|||
output.write(',');
|
||||
}
|
||||
output.write("\"");
|
||||
writeEscapedString(debugInfo.fileNames[i]);
|
||||
JsonUtil.writeEscapedString(output, debugInfo.fileNames[i]);
|
||||
output.write("\"");
|
||||
}
|
||||
output.write("]");
|
||||
|
@ -85,35 +86,6 @@ class SourceMapsWriter {
|
|||
first = false;
|
||||
}
|
||||
|
||||
private void writeEscapedString(String str) throws IOException {
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
char c = str.charAt(i);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
output.write("\\n");
|
||||
break;
|
||||
case '\r':
|
||||
output.write("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
output.write("\\t");
|
||||
break;
|
||||
case '\b':
|
||||
output.write("\\b");
|
||||
break;
|
||||
case '\\':
|
||||
output.write("\\\\");
|
||||
break;
|
||||
case '"':
|
||||
output.write("\\\"");
|
||||
break;
|
||||
default:
|
||||
output.write(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeVLQ(int number) throws IOException {
|
||||
if (number < 0) {
|
||||
number = ((-number) << 1) | 1;
|
||||
|
|
|
@ -37,33 +37,42 @@ public final class ExceptionHandling {
|
|||
@Unmanaged
|
||||
public static native void abort();
|
||||
|
||||
@Unmanaged
|
||||
private static native boolean isObfuscated();
|
||||
|
||||
@Unmanaged
|
||||
public static void printStack() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
while (location != null) {
|
||||
MethodLocation methodLocation = location != null ? location.method : null;
|
||||
|
||||
Console.printString(" at ");
|
||||
if (methodLocation.className == null || methodLocation.methodName == null) {
|
||||
Console.printString("(Unknown method)");
|
||||
} else {
|
||||
Console.printString(methodLocation.className.value);
|
||||
Console.printString(".");
|
||||
Console.printString(methodLocation.methodName.value);
|
||||
}
|
||||
Console.printString("(");
|
||||
if (methodLocation.fileName != null && location.lineNumber >= 0) {
|
||||
Console.printString(methodLocation.fileName.value);
|
||||
Console.printString(":");
|
||||
Console.printInt(location.lineNumber);
|
||||
}
|
||||
if (isObfuscated()) {
|
||||
Console.printString(" at Obfuscated.obfuscated(Obfuscated.java:");
|
||||
Console.printInt(callSiteId);
|
||||
Console.printString(")\n");
|
||||
} else {
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
while (location != null) {
|
||||
MethodLocation methodLocation = location != null ? location.method : null;
|
||||
|
||||
location = location.next;
|
||||
Console.printString(" at ");
|
||||
if (methodLocation.className == null || methodLocation.methodName == null) {
|
||||
Console.printString("(Unknown method)");
|
||||
} else {
|
||||
Console.printString(methodLocation.className.value);
|
||||
Console.printString(".");
|
||||
Console.printString(methodLocation.methodName.value);
|
||||
}
|
||||
Console.printString("(");
|
||||
if (methodLocation.fileName != null && location.lineNumber >= 0) {
|
||||
Console.printString(methodLocation.fileName.value);
|
||||
Console.printString(":");
|
||||
Console.printInt(location.lineNumber);
|
||||
}
|
||||
Console.printString(")\n");
|
||||
|
||||
location = location.next;
|
||||
}
|
||||
}
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
|
@ -137,14 +146,14 @@ public final class ExceptionHandling {
|
|||
}
|
||||
|
||||
@Unmanaged
|
||||
public static int callStackSize() {
|
||||
private static int callStackSize() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
int size = 0;
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
if (location == null) {
|
||||
if (isObfuscated() || location == null) {
|
||||
size++;
|
||||
} else {
|
||||
while (location != null) {
|
||||
|
@ -159,27 +168,35 @@ public final class ExceptionHandling {
|
|||
}
|
||||
|
||||
@Unmanaged
|
||||
public static void fillStackTrace(StackTraceElement[] target) {
|
||||
public static StackTraceElement[] fillStackTrace() {
|
||||
Address stackFrame = ShadowStack.getStackTop();
|
||||
int size = callStackSize();
|
||||
|
||||
ShadowStack.allocStack(1);
|
||||
StackTraceElement[] target = new StackTraceElement[size];
|
||||
ShadowStack.registerGCRoot(0, target);
|
||||
|
||||
int index = 0;
|
||||
while (stackFrame != null) {
|
||||
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||
CallSite callSite = findCallSiteById(callSiteId, stackFrame);
|
||||
CallSiteLocation location = callSite.location;
|
||||
if (location == null) {
|
||||
target[index++] = createElement("", "", null, location.lineNumber);
|
||||
if (isObfuscated()) {
|
||||
target[index++] = new StackTraceElement("Obfuscated", "obfuscated", "Obfuscated.java", callSiteId);
|
||||
} else if (location == null) {
|
||||
target[index++] = new StackTraceElement("", "", null, location.lineNumber);
|
||||
} else {
|
||||
while (location != null) {
|
||||
MethodLocation methodLocation = location.method;
|
||||
StackTraceElement element;
|
||||
if (methodLocation != null) {
|
||||
element = createElement(
|
||||
element = new StackTraceElement(
|
||||
methodLocation.className != null ? methodLocation.className.value : "",
|
||||
methodLocation.methodName != null ? methodLocation.methodName.value : "",
|
||||
methodLocation.fileName != null ? methodLocation.fileName.value : null,
|
||||
location.lineNumber);
|
||||
} else {
|
||||
element = createElement("", "", null, location.lineNumber);
|
||||
element = new StackTraceElement("", "", null, location.lineNumber);
|
||||
}
|
||||
target[index++] = element;
|
||||
location = location.next;
|
||||
|
@ -187,10 +204,8 @@ public final class ExceptionHandling {
|
|||
}
|
||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||
}
|
||||
}
|
||||
ShadowStack.releaseStack(1);
|
||||
|
||||
private static StackTraceElement createElement(String className, String methodName, String fileName,
|
||||
int lineNumber) {
|
||||
return new StackTraceElement(className, methodName, fileName, lineNumber);
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,4 +57,8 @@
|
|||
|
||||
#ifndef TEAVM_GC_LOG
|
||||
#define TEAVM_GC_LOG 0
|
||||
#endif
|
||||
|
||||
#ifndef TEAVM_OBFUSCATED
|
||||
#define TEAVM_OBFUSCATED 0
|
||||
#endif
|
|
@ -342,6 +342,7 @@ public class TeaVMTool {
|
|||
cTarget.setLineNumbersGenerated(debugInformationGenerated);
|
||||
cTarget.setLongjmpUsed(longjmpSupported);
|
||||
cTarget.setHeapDump(heapDump);
|
||||
cTarget.setObfuscated(minifying);
|
||||
return cTarget;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user