diff --git a/doc/soot_options.htm b/doc/soot_options.htm index 953691238d5..79c1a03d4a6 100644 --- a/doc/soot_options.htm +++ b/doc/soot_options.htm @@ -597,7 +597,9 @@

Input Options

-src-prec format
- c  + cache  +
only-cache  +
class 
only-class 
J  diff --git a/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java b/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java index 3b7d69cc04a..f0695d81bb9 100644 --- a/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java +++ b/eclipse/ca.mcgill.sable.soot/src/ca/mcgill/sable/soot/ui/PhaseOptionsDialog.java @@ -8772,6 +8772,14 @@ private Composite Input_OptionsCreate(Composite parent) { data = new OptionData [] { + new OptionData("Cache", + "cache", + "\nTry to resolve classes first from the shared class cache found \nin the Soot classpath. Fall back to .class and then .jimple \nfiles only when unable to find a class in the cache.", + false), + new OptionData("Only Cache Source", + "only-cache", + "\nTry to resolve classes first from the shared class cache found \nin the Soot classpath. Do not try any other types of files even \nwhen unable to find a class in the cache.", + false), new OptionData("Class File", "c class", "\nTry to resolve classes first from .class files found in the Soot \nclasspath. Fall back to .jimple files only when unable to find a \n.class file.", diff --git a/pom.xml b/pom.xml index d3cd5eed855..72df988a72c 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,8 @@ https://github.com/soot-oss/soot + /root/openj9-openjdk-jdk8/build/linux-x86_64-normal-server-release + cp.txt 1.8 1.8 1.0.2 @@ -78,6 +80,42 @@ sootclasses-trunk + + org.codehaus.gmaven + gmaven-plugin + 1.4 + + + generate-resources + + execute + + + + def file = new File(project.properties.cpfile) + project.properties.cp = file.getText() + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.9 + + + build-classpath + generate-sources + + build-classpath + + + ${cpfile} + + + + org.codehaus.mojo xml-maven-plugin @@ -255,8 +293,23 @@ maven-compiler-plugin ${maven-compiler-plugin.version} - ${maven.compiler.source} - ${maven.compiler.target} + true + true + ${openj9-build-root}/images/j2sdk-image/bin/javac + 1.8 + + -classpath${cp}:${openj9-build-root}/images/j2sdk-image/jre/lib/ddr/j9ddr.jar + -d${basedir}/target/classes + -s${basedir}/target/generated-sources/annotations + -g + -verbose + -nowarn + -target1.8 + -source1.8 + -encodingUTF-8 + + ${maven.compiler.source} + ${maven.compiler.target} diff --git a/src/main/generated/options/soot/AntTask.java b/src/main/generated/options/soot/AntTask.java index 4475909e4b9..0cd68456b29 100644 --- a/src/main/generated/options/soot/AntTask.java +++ b/src/main/generated/options/soot/AntTask.java @@ -253,6 +253,8 @@ public void setast_metrics(boolean arg) { public void setsrc_prec(String arg) { if(false + || arg.equals( "cache" ) + || arg.equals( "only-cache" ) || arg.equals( "c" ) || arg.equals( "class" ) || arg.equals( "only-class" ) diff --git a/src/main/generated/options/soot/options/Options.java b/src/main/generated/options/soot/options/Options.java index decdc944e25..f37219a874a 100644 --- a/src/main/generated/options/soot/options/Options.java +++ b/src/main/generated/options/soot/options/Options.java @@ -42,15 +42,17 @@ public static Options v() { return G.v().soot_options_Options(); } - public static final int src_prec_c = 1; - public static final int src_prec_class = 1; - public static final int src_prec_only_class = 2; - public static final int src_prec_J = 3; - public static final int src_prec_jimple = 3; - public static final int src_prec_java = 4; - public static final int src_prec_apk = 5; - public static final int src_prec_apk_class_jimple = 6; - public static final int src_prec_apk_c_j = 6; + public static final int src_prec_cache = 1; + public static final int src_prec_only_cache = 2; + public static final int src_prec_c = 3; + public static final int src_prec_class = 3; + public static final int src_prec_only_class = 4; + public static final int src_prec_J = 5; + public static final int src_prec_jimple = 5; + public static final int src_prec_java = 6; + public static final int src_prec_apk = 7; + public static final int src_prec_apk_class_jimple = 8; + public static final int src_prec_apk_c_j = 8; public static final int output_format_J = 1; public static final int output_format_jimple = 1; public static final int output_format_j = 2; @@ -368,6 +370,24 @@ else if (false String value = nextOption(); if (false); + else if (false + || value.equals("cache") + ) { + if (src_prec != 0 && src_prec != src_prec_cache) { + G.v().out.println("Multiple values given for option " + option); + return false; + } + src_prec = src_prec_cache; + } + else if (false + || value.equals("only-cache") + ) { + if (src_prec != 0 && src_prec != src_prec_only_cache) { + G.v().out.println("Multiple values given for option " + option); + return false; + } + src_prec = src_prec_only_cache; + } else if (false || value.equals("c") || value.equals("class") @@ -1742,6 +1762,8 @@ public String getUsage() { + padOpt("-force-android-jar ARG", "Force Soot to use ARG as the path for the android.jar file.") + padOpt("-ast-metrics", "Compute AST Metrics if performing java to jimple") + padOpt("-src-prec ARG", "Sets source precedence to ARG files") + + padVal("cache", "Favour shared class cache as Soot source") + + padVal("only-cache", "Use only shared class cache as Soot source") + padVal("c class (default)", "Favour class files as Soot source") + padVal("only-class", "Use only class files as Soot source") + padVal("J jimple", "Favour Jimple files as Soot source") diff --git a/src/main/java/soot/SourceLocator.java b/src/main/java/soot/SourceLocator.java index 721bc8df1cd..ae46af76d3e 100755 --- a/src/main/java/soot/SourceLocator.java +++ b/src/main/java/soot/SourceLocator.java @@ -49,6 +49,7 @@ import soot.JavaClassProvider.JarException; import soot.asm.AsmClassProvider; import soot.asm.AsmJava9ClassProvider; +import soot.cache.CacheClassProvider; import soot.dexpler.DexFileProvider; import soot.options.Options; @@ -245,6 +246,17 @@ protected void setupClassProviders() { classProviders.add(new AsmJava9ClassProvider()); } switch (Options.v().src_prec()) { + case Options.src_prec_cache: + System.out.println("Using the cache with default source provider chain."); + classProviders.add(new CacheClassProvider()); + classProviders.add(classFileClassProvider); + classProviders.add(new JimpleClassProvider()); + classProviders.add(new JavaClassProvider()); + break; + case Options.src_prec_only_cache: + System.out.println("Using the cache only source provider chain."); + classProviders.add(new CacheClassProvider()); + break; case Options.src_prec_class: classProviders.add(classFileClassProvider); classProviders.add(new JimpleClassProvider()); diff --git a/src/main/java/soot/asm/SootClassBuilder.java b/src/main/java/soot/asm/SootClassBuilder.java index 3b68588bbe9..e999ef3f3bf 100644 --- a/src/main/java/soot/asm/SootClassBuilder.java +++ b/src/main/java/soot/asm/SootClassBuilder.java @@ -79,7 +79,7 @@ public class SootClassBuilder extends ClassVisitor { * @param klass * Soot class to build. */ - protected SootClassBuilder(SootClass klass) { + public SootClassBuilder(SootClass klass) { super(Opcodes.ASM8); this.klass = klass; this.deps = new HashSet(); @@ -97,6 +97,10 @@ protected SootClass getKlass() { return klass; } + public Set getDeps() { + return deps; + } + void addDep(String s) { String className = AsmUtil.baseTypeName(s); RefType refType = makeRefType(className); diff --git a/src/main/java/soot/cache/CacheClassProvider.java b/src/main/java/soot/cache/CacheClassProvider.java new file mode 100644 index 00000000000..dc1ec1a9588 --- /dev/null +++ b/src/main/java/soot/cache/CacheClassProvider.java @@ -0,0 +1,156 @@ +package soot.cache; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2020 Kristen Newbury + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.ibm.oti.shared.HelperAlreadyDefinedException; +import com.ibm.oti.shared.Shared; +import com.ibm.oti.shared.SharedClassHelperFactory; +import com.ibm.oti.shared.SharedClassURLClasspathHelper; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import soot.ClassProvider; +import soot.ClassSource; + +/** + * OpenJ9 Shared Cache Class class provider. + * + * @author Kristen Newbury + */ + +public class CacheClassProvider implements ClassProvider { + + private static URL testClassUrl; + + public CacheClassProvider() { + if (!System.getProperty("java.vm.name").contains("OpenJ9")) { + throw new RuntimeException("CacheClassProvider feature only works with OpenJ9 JVM running Soot"); + } + } + + public static void setTestClassUrl(String url) { + try { + testClassUrl = new URL("file://" + url); + System.out.println("CacheClassProvider: setting testClassUrl: " + url); + } catch (MalformedURLException e) { + System.out.println("Bad URL provided, not using url: " + url); + e.printStackTrace(); + } + } + + public ClassSource find(String cls) { + + byte[] romCookie = null; + CacheMemorySingleton cacheMem = null; + ByteBuffer wrapper = null; + + SharedClassHelperFactory factory = Shared.getSharedClassHelperFactory(); + if (factory != null) { + URL[] urlsForRuntime = null; + URL[] urlsForApp = null; + URL[] urlsForJCE = null; + URL jceurl = null; + URL rturl = null; + SharedClassURLClasspathHelper helperForAppClasses = null; + SharedClassURLClasspathHelper helperForRuntimeClasses = null; + SharedClassURLClasspathHelper helperForJCE = null; + try { + rturl = new URL("file://" + System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar"); + jceurl = new URL("file://" + System.getProperty("java.home") + File.separator + "lib" + File.separator + "jce.jar"); + urlsForRuntime = new URL[] { rturl }; + if (testClassUrl != null) { + urlsForApp = new URL[] { testClassUrl }; + } else { + System.out.println("No test class url set"); + return null; + } + + urlsForJCE = new URL[] { jceurl }; + } catch (MalformedURLException e) { + System.out.println("Bad URL provided"); + e.printStackTrace(); + } + URLClassLoader loaderForRuntime = new URLClassLoader(urlsForRuntime); + URLClassLoader loaderForApp = new URLClassLoader(urlsForApp); + URLClassLoader loaderForJCE = new URLClassLoader(urlsForJCE); + + // get helper to find classes in cache + try { + helperForRuntimeClasses = factory.getURLClasspathHelper(loaderForRuntime, urlsForRuntime); + helperForAppClasses = factory.getURLClasspathHelper(loaderForApp, urlsForApp); + helperForJCE = factory.getURLClasspathHelper(loaderForJCE, urlsForJCE); + } catch (HelperAlreadyDefinedException e) { + System.out.println("Helper already defined?" + e.getMessage()); + e.printStackTrace(); + } + + helperForAppClasses.confirmAllEntries(); + helperForRuntimeClasses.confirmAllEntries(); + helperForJCE.confirmAllEntries(); + + try { + // for now this part happens every time + // maybe consider avoiding that later + byte[] cacheInfo = helperForAppClasses.findSharedCache(); + if (cacheInfo != null) { + wrapper = ByteBuffer.wrap(cacheInfo); + // jni filled byte array + wrapper.order(ByteOrder.nativeOrder()); + cacheMem = CacheMemorySingleton.getInstance(); + } else { + System.out.println("Cannot get cache start"); + } + } catch (Exception e) { + System.out.println(e.getMessage()); + e.printStackTrace(); + } + + romCookie = helperForAppClasses.findSharedClass(cls, null); + if (romCookie == null) { + romCookie = helperForRuntimeClasses.findSharedClass(cls, null); + if (romCookie == null) { + romCookie = helperForJCE.findSharedClass(cls, null); + if (romCookie == null) { + System.out.println("Cannot find class in cache: " + cls); + } else { + System.out.println("Located the class in the cache: " + cls); + } + } else { + System.out.println("Located the class in the cache: " + cls); + } + } else { + System.out.println("Located the class in the cache: " + cls); + } + + } else { + System.out.println("Cache helper null, cannot find class in cache: " + cls); + System.out.println("Is Shared Class Cache enabled on command line?"); + } + return romCookie == null ? null : new CacheClassSource(cls, romCookie, cacheMem, wrapper.getLong(), wrapper.getInt()); + } +} diff --git a/src/main/java/soot/cache/CacheClassSource.java b/src/main/java/soot/cache/CacheClassSource.java new file mode 100644 index 00000000000..e1f2f342c8e --- /dev/null +++ b/src/main/java/soot/cache/CacheClassSource.java @@ -0,0 +1,125 @@ +package soot.cache; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2020 Kristen Newbury + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.ibm.j9ddr.IVMData; +import com.ibm.j9ddr.VMDataFactory; +import com.ibm.j9ddr.corereaders.memory.IProcess; + +import java.io.IOException; +import java.io.InputStream; + +import soot.ClassSource; +import soot.SootClass; +import soot.asm.SootClassBuilder; +import soot.javaToJimple.IInitialResolver.Dependencies; + +/** + * Cache class source implementation. + * + * @author Kristen Newbury + */ + +public class CacheClassSource extends ClassSource { + + private byte[] cookiesource; + private byte[] classsource; + private CacheMemorySingleton memory; + private long cacheaddr; + private int cachesize; + + /** + * Constructs a new Cache class source. + * + * @param cls + * fully qualified name of the class. + * @param data + * stream containing data for class. + */ + CacheClassSource(String cls, byte[] source, CacheMemorySingleton memory, long cacheaddr, int cachesize) { + super(cls); + if (source == null) { + throw new IllegalStateException("Error: The class source must not be null."); + } + this.cookiesource = source; + this.memory = memory; + this.cacheaddr = cacheaddr; + this.cachesize = cachesize; + } + + @Override + public Dependencies resolve(SootClass sc) { + InputStream d = null; + try { + + // ideally replace index (24) into cookie with the runtime val for offset of romclass address + // but no current way to know that value from the scc/jvm side + long addr = 0; + for (int i = 0; i < 8; i++) { + addr += ((long) cookiesource[i + 24] & 0xffL) << (8 * i); + } + + Dependencies deps = new Dependencies(); + SootClassBuilder scb = new SootClassBuilder(sc); + tryWithMemModel(addr, scb); + deps.typesToSignature.addAll(scb.getDeps()); + return deps; + } catch (Exception e) { + throw new RuntimeException("Error: Failed to create class reader from class source.", e); + } finally { + try { + if (d != null) { + d.close(); + d = null; + } + } catch (IOException e) { + throw new RuntimeException("Error: Failed to close source input stream.", e); + } finally { + close(); + } + } + } + + void tryWithMemModel(long addr, SootClassBuilder scb) { + + IProcess proc = (IProcess) memory.getMemory(); + try { + // setup DDR - init datatype + assert proc != null : "Process should not be null"; + IVMData aVMData = VMDataFactory.getVMData(proc); + assert aVMData != null : "VMDATA should not be null"; + + // now add the memory source + memory.addMemorySource(this.cacheaddr, this.cachesize); + + // can force our wrapper to be loaded by J9DDRClassLoader + // additionally, this is why this must be built with OpenJ9 JVM + aVMData.bootstrap("com.ibm.j9ddr.vm29.ROMClassWrapper", new Object[] { addr, scb, memory }); + + } catch (Exception e) { + System.out.println("Could not setup ddr" + e.getMessage()); + e.printStackTrace(System.out); + } + + } +} diff --git a/src/main/java/soot/cache/CacheMemorySingleton.java b/src/main/java/soot/cache/CacheMemorySingleton.java new file mode 100644 index 00000000000..84892f5f59a --- /dev/null +++ b/src/main/java/soot/cache/CacheMemorySingleton.java @@ -0,0 +1,97 @@ +package soot.cache; + +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2020 Kristen Newbury + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.ibm.j9ddr.corereaders.memory.BufferedMemory; +import com.ibm.j9ddr.corereaders.memory.BufferedMemorySource; + +import java.lang.reflect.Field; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +//@author Kristen Newbury + +public class CacheMemorySingleton { + + // this singleton can only have one memory source ever added to it + private static CacheMemorySingleton cacheMemorySingleton; + private BufferedMemory memory; + private BufferedMemorySource memorySource; + + private CacheMemorySingleton() { + memory = new BufferedMemory(ByteOrder.nativeOrder()); + } + + public static CacheMemorySingleton getInstance() { + if (cacheMemorySingleton == null) { + cacheMemorySingleton = new CacheMemorySingleton(); + } + return cacheMemorySingleton; + } + + public BufferedMemory getMemory() { + return memory; + } + + public BufferedMemorySource getMemorySource() { + return memorySource; + } + + public void addMemorySource(long addr, int size) { + if (cacheMemorySingleton != null && memorySource == null) { + ByteBuffer bb = makeCacheBuffer(addr, size); + memorySource = new BufferedMemorySource(addr, bb); + memory.addMemorySource(memorySource); + } + } + + static final Field address; + static final Field capacity; + + static { + // first set the buffer to be configurable + try { + address = Buffer.class.getDeclaredField("address"); + address.setAccessible(true); + capacity = Buffer.class.getDeclaredField("capacity"); + capacity.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new AssertionError(e); + } + } + + private ByteBuffer makeCacheBuffer(long addr, int size) { + // then set it up + try { + ByteBuffer bb = ByteBuffer.allocateDirect(0).order(ByteOrder.nativeOrder()); + address.setLong(bb, addr); + capacity.setInt(bb, size); + bb.clear(); + return bb; + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + +} diff --git a/src/main/java/soot/cache/ROMClassWrapper.java b/src/main/java/soot/cache/ROMClassWrapper.java new file mode 100644 index 00000000000..a3eaba87d28 --- /dev/null +++ b/src/main/java/soot/cache/ROMClassWrapper.java @@ -0,0 +1,947 @@ +/******************************************************************************* + * Copyright (c) 2010, 2019 IBM Corp. and others + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] http://openjdk.java.net/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception + *******************************************************************************/ +package com.ibm.j9ddr.vm29; + +import com.ibm.j9ddr.CorruptDataException; +import com.ibm.j9ddr.IBootstrapRunnable; +import com.ibm.j9ddr.IVMData; +import com.ibm.j9ddr.InvalidDataTypeException; +import com.ibm.j9ddr.corereaders.memory.BufferedMemorySource; +import com.ibm.j9ddr.corereaders.memory.MemoryFault; +import com.ibm.j9ddr.vm29.j9.BCNames; +import com.ibm.j9ddr.vm29.j9.ConstantPoolHelpers; +import com.ibm.j9ddr.vm29.j9.J9ROMFieldShapeIterator; +import com.ibm.j9ddr.vm29.j9.ROMHelp; +import com.ibm.j9ddr.vm29.pointer.FloatPointer; +import com.ibm.j9ddr.vm29.pointer.I64Pointer; +import com.ibm.j9ddr.vm29.pointer.SelfRelativePointer; +import com.ibm.j9ddr.vm29.pointer.U16Pointer; +import com.ibm.j9ddr.vm29.pointer.U32Pointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ExceptionInfoPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMClassPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMClassRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMConstantPoolItemPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMFieldRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMFieldShapePointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMMethodHandleRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMMethodPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMMethodRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMMethodTypeRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMNameAndSignaturePointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMSingleSlotConstantRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMStaticFieldShapePointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9ROMStringRefPointer; +import com.ibm.j9ddr.vm29.pointer.generated.J9UTF8Pointer; +import com.ibm.j9ddr.vm29.pointer.helper.J9ROMClassHelper; +import com.ibm.j9ddr.vm29.pointer.helper.J9ROMMethodHelper; +import com.ibm.j9ddr.vm29.pointer.helper.J9UTF8Helper; +import com.ibm.j9ddr.vm29.types.UDATA; + +import java.nio.ByteOrder; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import soot.asm.SootClassBuilder; +import soot.cache.CacheMemorySingleton; + +/* + * This implementation strongly relies upon the visitor invoke pattern defined in ASM Classreader: + * https://gitlab.ow2.org/asm/asm/blob/ASM_5_2/src/org/objectweb/asm/ClassReader.java + * HOWEVER, did not want inheritance bc need to avoid asm ClassReader constructor behaviour which relies upon + * many hardcoded indices + */ + +public class ROMClassWrapper implements IBootstrapRunnable { + + // might want to fix. currently stolen hardcode from + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/oti/j9nonbuilder.h#L1965 + // the reason is that these are only defined in: com.ibm.j9ddr.vm29.structure.J9BCTranslationData + // and only sizeof is exposed in: + // openj9/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/pointer/generated/J9DescriptionBitsPointer.java + // though somehow bytecode dumper can use ... + // https://github.com/eclipse/openj9/blob/v0.14.0-release/debugtools/DDR_VM/src/com/ibm/j9ddr/vm29/tools/ddrinteractive/commands/ByteCodeDumper.java#L96 + // but we are using the structure file, not a loaded class... so this may be the reason... + + private int BCT_J9DescriptionCpTypeScalar = 0; + private int BCT_J9DescriptionCpTypeObject = 1; + private int BCT_J9DescriptionCpTypeClass = 2; + private int J9DescriptionCpTypeShift = 4; + private int J9AccStatic = 8; + + private int J9CPTYPE_CLASS = 1; + private int J9CPTYPE_STRING = 2; + private int J9CPTYPE_INT = 3; + private int J9CPTYPE_FLOAT = 4; + private int J9CPTYPE_LONG = 5; + private int J9CPTYPE_DOUBLE = 6; + private int J9CPTYPE_FIELD = 7; + private int J9CPTYPE_INSTANCE_METHOD = 9; + private int J9CPTYPE_STATIC_METHOD = 10; + private int J9CPTYPE_HANDLE_METHOD = 11; + private int J9CPTYPE_INTERFACE_METHOD = 12; + private int J9CPTYPE_METHOD_TYPE = 13; + private int J9CPTYPE_METHODHANDLE = 14; + + // https://github.com/eclipse/openj9/blob/master/runtime/oti/cfr.h + // CFR_ACC_PUBLIC | CFR_ACC_PRIVATE | CFR_ACC_PROTECTED | CFR_ACC_STATIC | CFR_ACC_FINAL | CFR_ACC_SYNCHRONIZED | + // CFR_ACC_BRIDGE | CFR_ACC_VARARGS | CFR_ACC_NATIVE | CFR_ACC_STRICT | CFR_ACC_ABSTRACT | CFR_ACC_SYNTHETIC) + private int METHOD_ACCESS_MASK = 0x00000001 | 0x00000002 | 0x00000004 | 0x00000008 | 0x00000010 | 0x00000020 | 0x00000040 + | 0x00000080 | 0x00000100 | 0x00000800 | 0x00000400 | 0x00001000; + + // J9FieldFlagConstant 0x400000 + // J9FieldTypeDouble 0x180000 + // J9FieldTypeLong 0x380000 + // J9FieldTypeFloat 0x100000 + // J9FieldFlagObject 0x20000 + // J9FieldTypeByte 0x200000 + + // likely less efficient than hardcoding the ints, but this is easier to read and check since the vals in nonbuilder + // are in hex + private int J9FieldTypeDouble = Integer.parseInt("180000", 16); + private int J9FieldTypeLong = Integer.parseInt("380000", 16); + private int J9FieldFlagObject = Integer.parseInt("20000", 16); + private int J9FieldFlagConstant = Integer.parseInt("400000", 16); + private int J9FieldTypeFloat = Integer.parseInt("100000", 16); + private int J9FieldTypeMask = Integer.parseInt("380000", 16); + private int J9FieldTypeByte = Integer.parseInt("200000", 16); + ///////////////////////////////////// + + private J9ROMClassPointer pointer; + private CacheMemorySingleton cacheMem; + private static ClassVisitor classVisitor; + + // classreader specific attributes + static final boolean FRAMES = true; + + public void run(IVMData vmData, Object[] userData) { + + Long addr = new Long((long) userData[0]); + this.pointer = J9ROMClassPointer.cast(addr); + + this.classVisitor = (SootClassBuilder) userData[1]; + + // need this for method iter, getting a bit much to have everyone here + this.cacheMem = (CacheMemorySingleton) userData[2]; + + accept(this.classVisitor); + } + + public static ClassVisitor getClassVisitor() { + return classVisitor; + } + + public void accept(final ClassVisitor classVisitor) { + + try { + int version = pointer.majorVersion().intValue(); + int classModifiers = pointer.modifiers().intValue(); + String classname = J9UTF8Helper.stringValue(pointer.className()); + J9UTF8Pointer superclassPointer = pointer.superclassName(); + String superclassname; + // java.lang.Object has no superclass + if (superclassPointer == J9UTF8Pointer.NULL) { + superclassname = null; + } else { + superclassname = J9UTF8Helper.stringValue(superclassPointer); + } + + // reference to constant pool + J9ROMConstantPoolItemPointer constantPool = J9ROMClassHelper.constantPool(pointer); + + // header class info + // version, int access, String name, String signature, String superName, String[] interfaces + classVisitor.visit(version, classModifiers, classname, "", superclassname, new String[] {}); + + readFields(classVisitor, constantPool); + + // method handling + int methodCount = pointer.romMethodCount().intValue(); + J9ROMMethodPointer romMethod = pointer.romMethods(); + for (int i = 0; i < methodCount; i++) { + readMethod(romMethod, constantPool); + romMethod = ROMHelp.nextROMMethod(romMethod); + } + + } catch (Exception e) { + System.out.println("Issue in visitor pattern driving: " + e.getMessage()); + e.printStackTrace(System.out); + } + // finish up + classVisitor.visitEnd(); + } + + void readFields(ClassVisitor classVisitor, J9ROMConstantPoolItemPointer constantPool) throws CorruptDataException { + + FieldVisitor fv = null; + Object value = null; + + // FieldVisitor visitField(int access, String name, String desc, String signature, Object value + UDATA romFieldCount = pointer.romFieldCount(); + J9ROMFieldShapeIterator iterator = new J9ROMFieldShapeIterator(pointer.romFields(), romFieldCount); + J9ROMFieldShapePointer currentField = null; + + while (iterator.hasNext()) { + currentField = (J9ROMFieldShapePointer) iterator.next(); + + String name = J9UTF8Helper.stringValue(currentField.nameAndSignature().name()); + String signature = J9UTF8Helper.stringValue(currentField.nameAndSignature().signature()); + + if (!currentField.modifiers().bitAnd(J9AccStatic).eq(0)) { + + // if its static, we should get its initial value: + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/oti/j9nonbuilder.h#L686 + + value = getStaticFieldValue(currentField.modifiers(), J9ROMStaticFieldShapePointer.cast(currentField), constantPool); + + } + + // visitField(int access, String name, String desc, String signature, Object value) + fv = classVisitor.visitField(currentField.modifiers().intValue(), name, signature, signature, value); + fv.visitEnd(); + value = null; + } + + } + + void readMethod(J9ROMMethodPointer romMethod, J9ROMConstantPoolItemPointer constantPool) throws CorruptDataException { + // method info + + int methodModifiers = romMethod.modifiers().intValue(); + J9ROMNameAndSignaturePointer nameAndSignature = romMethod.nameAndSignature(); + String name = J9UTF8Helper.stringValue(nameAndSignature.name()); + String signature = J9UTF8Helper.stringValue(nameAndSignature.signature()); + + // for debugging only + /* + * if(name.contains("insert-some-method-name-here")){ System.out.println("---------------------------------"); + * System.out.println("DUMPING copyof"); long dumpFlags = (this.cacheMem.getMemorySource().getByteOrder() == + * ByteOrder.BIG_ENDIAN) ? 1 : 0; J9BCUtil.j9bcutil_dumpRomMethod(System.out, romMethod, pointer, dumpFlags, + * J9BCUtil.BCUtil_DumpAnnotations); System.out.println("---------------------------------"); } + */ + + int maxStack = romMethod.maxStack().intValue(); + int maxLocals = romMethod.tempCount().intValue() + romMethod.argCount().intValue(); + int argCount = romMethod.argCount().intValue(); + + int romMethodSize = J9ROMMethodHelper.bytecodeSize(romMethod).intValue(); + + long bytecodeSt = J9ROMMethodHelper.bytecodes(romMethod).longValue(); + long bytecodeEnd = J9ROMMethodHelper.bytecodeEnd(romMethod).longValue(); + char returnType = signature.charAt(signature.lastIndexOf(")") + 1); + + String[] exceptions = getExceptions(romMethod); + + MethodVisitor mv + = classVisitor.visitMethod(methodModifiers & METHOD_ACCESS_MASK, name, signature, signature, exceptions); + readMethodBody(bytecodeSt, bytecodeEnd, mv, constantPool, returnType); + // for now + mv.visitMaxs(maxStack, maxLocals); + mv.visitEnd(); + } + + String[] getExceptions(J9ROMMethodPointer romMethod) throws CorruptDataException { + + if (J9ROMMethodHelper.hasExceptionInfo(romMethod)) { + + J9ExceptionInfoPointer exceptionData = ROMHelp.J9_EXCEPTION_DATA_FROM_ROM_METHOD(romMethod); + long throwCount = exceptionData.throwCount().longValue(); + String[] exceptions = new String[(int) throwCount]; + + if (throwCount > 0) { + SelfRelativePointer currentThrowName = ROMHelp.J9EXCEPTIONINFO_THROWNAMES(exceptionData); + for (int i = 0; i < throwCount; i++) { + exceptions[i] = J9UTF8Helper.stringValue(J9UTF8Pointer.cast(currentThrowName.get())); + } + } + return exceptions; + } + return new String[] {}; + } + + Object getStaticFieldValue(UDATA modsFull, J9ROMStaticFieldShapePointer field, J9ROMConstantPoolItemPointer constantPool) + throws CorruptDataException { + + // get just the type portion of the mods + int mods = modsFull.intValue() & J9FieldTypeMask; + + // aka if not a null static + if (modsFull.anyBitsIn(J9FieldFlagConstant)) { + + if (mods == J9FieldTypeLong) { + return new Long(field.initialValue().longValue()); + + } else if (mods == J9FieldTypeDouble) { + + // field.initialValue is setup like a cp entry, even longValue() +double cast cannot get this as double + // so we manually read it, even though that relies on hard offsets + BufferedMemorySource src = this.cacheMem.getMemorySource(); + long first = I64Pointer.cast(field.add(1)).at(0).longValue(); + long second = I64Pointer.cast(field.add(2)).at(0).longValue(); + // This is honestly the opposite of what we expect it to be, completely unsure of why + long constantvalue = (src.getByteOrder() == ByteOrder.BIG_ENDIAN) ? (second << 32) | (first & 0xffffffffL) + : (first << 32) | (second & 0xffffffffL); + + return new Double(Double.longBitsToDouble(constantvalue)); + } + + else if (modsFull.allBitsIn(J9FieldFlagObject)) { + // this initial value is actually an index into the constant pool + J9ROMConstantPoolItemPointer info = constantPool.add(field.initialValue().intValue()); + String value = J9UTF8Helper.stringValue(J9ROMStringRefPointer.cast(info).utf8Data()); + return value; + + } else { + + /* by default, type is anything that can be read as an int, except for float */ + if (mods == J9FieldTypeFloat) { + return new Float(Float.intBitsToFloat((int) field.initialValue().longValue())); + } else if (mods == J9FieldTypeByte) { + return new Byte(field.initialValue().byteValue()); + } else { + try { + return new Integer(field.initialValue().intValue()); + } catch (InvalidDataTypeException e) { + return new Integer((int) field.initialValue().longValue()); + } + } + } + } else { + return null; + } + + } + + void readMethodBody(long bytecodeSt, long bytecodeEnd, MethodVisitor mv, J9ROMConstantPoolItemPointer constantPool, + char returnType) throws CorruptDataException { + // drives the visitor to define the body of the method + BufferedMemorySource src = this.cacheMem.getMemorySource(); + long ptr = bytecodeSt; + + // for our targets, as we find them + Label[] labels = findLabels(bytecodeSt, bytecodeEnd, ptr, src); + + while (ptr < bytecodeEnd) { + int offset = (int) (ptr - bytecodeSt); + int opcode = (int) (src.getByte(ptr) & 0xFF); + + // visit a label if there is one + Label l = labels[offset]; + if (l != null) { + mv.visitLabel(l); + } + + if ((opcode == BCNames.JBnop) || (opcode == BCNames.JBinvokeinterface2)) { + ptr += 1; + } else if ((opcode == BCNames.JBdefaultvalue) || (opcode == BCNames.JBwithfield)) { + // these only exist for #if defined(J9VM_OPT_VALHALLA_VALUE_TYPES)? + ptr += 2; + } else if (opcode == BCNames.JBiinc) { + int LVAindex = src.getByte(ptr + 1); + int increment = src.getByte(ptr + 2); + mv.visitIincInsn(LVAindex, increment); + ptr += 3; + } else if (opcode == BCNames.JBiincw) { + int LVAindex = src.getShort(ptr + 1); + int increment = src.getShort(ptr + 3); + mv.visitIincInsn(LVAindex, increment); + ptr += 6; + } else if (opcode == BCNames.JBmultianewarray) { + + int index = src.getShort(ptr + 1); + + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + int dim = src.getByte(ptr + 3) & 0xFF; + + String arrName = J9UTF8Helper.stringValue(J9ROMStringRefPointer.cast(info).utf8Data()); + mv.visitMultiANewArrayInsn(arrName, dim); + ptr += 4; + } + // maybe don't expect to see the returnFromConstructor: + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/compiler/ilgen/J9ByteCode.hpp + else if ((opcode == BCNames.JBreturnFromConstructor) || (opcode == BCNames.JBgenericReturn) + || (opcode == BCNames.JBreturn0)) { + mv.visitInsn(Opcodes.RETURN); + ptr += 1; + } else if ((opcode == BCNames.JBreturnC) || (opcode == BCNames.JBreturnS) || (opcode == BCNames.JBreturnB) + || (opcode == BCNames.JBreturnZ)) { + mv.visitInsn(Opcodes.IRETURN); + ptr += 1; + } else if ((opcode == BCNames.JBreturn1) || (opcode == BCNames.JBreturn2)) { + // return0,1,2 correspond to return(pop 0 slots), return(pop 1 slot) and return(pop 2 slots) + opcode = getReturnType(returnType); + mv.visitInsn(opcode); + ptr += 1; + } else if ((opcode == BCNames.JBinvokehandle) || (opcode == BCNames.JBinvokehandlegeneric) + || (opcode == BCNames.JBinvokestaticsplit) || (opcode == BCNames.JBinvokespecialsplit)) { + // TODO handle + ptr += 3; + } else if ((opcode == BCNames.JBiload0) || (opcode == BCNames.JBiload1) || (opcode == BCNames.JBiload2) + || (opcode == BCNames.JBiload3) || (opcode == BCNames.JBlload0) || (opcode == BCNames.JBlload1) + || (opcode == BCNames.JBlload2) || (opcode == BCNames.JBlload3) || (opcode == BCNames.JBfload0) + || (opcode == BCNames.JBfload1) || (opcode == BCNames.JBfload2) || (opcode == BCNames.JBfload3) + || (opcode == BCNames.JBdload0) || (opcode == BCNames.JBdload1) || (opcode == BCNames.JBdload2) + || (opcode == BCNames.JBdload3) || (opcode == BCNames.JBaload0) || (opcode == BCNames.JBaload1) + || (opcode == BCNames.JBaload2) || (opcode == BCNames.JBaload3) || + + (opcode == BCNames.JBistore0) || (opcode == BCNames.JBistore1) || (opcode == BCNames.JBistore2) + || (opcode == BCNames.JBistore3) || (opcode == BCNames.JBlstore0) || (opcode == BCNames.JBlstore1) + || (opcode == BCNames.JBlstore2) || (opcode == BCNames.JBlstore3) || (opcode == BCNames.JBfstore0) + || (opcode == BCNames.JBfstore1) || (opcode == BCNames.JBfstore2) || (opcode == BCNames.JBfstore3) + || (opcode == BCNames.JBdstore0) || (opcode == BCNames.JBdstore1) || (opcode == BCNames.JBdstore2) + || (opcode == BCNames.JBdstore3) || (opcode == BCNames.JBastore0) || (opcode == BCNames.JBastore1) + || (opcode == BCNames.JBastore2) || (opcode == BCNames.JBastore3)) { + if (opcode > Opcodes.ISTORE) { + + opcode -= 59; // ISTORE_0 + mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); + } else { + + opcode -= 26; + mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); + } + ptr += 1; + } else if (opcode == BCNames.JBaload0getfield) { + // handle this as an aload0, followed by a getfield (where the index is 2 bytes in?) + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/vm/BytecodeInterpreter.hpp#L6480 + mv.visitVarInsn(25, 0); + int index = src.getUnsignedShort(ptr + 2); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + J9ROMFieldRefPointer romFieldRef = J9ROMFieldRefPointer.cast(info); + String owner + = J9UTF8Helper.stringValue(J9ROMClassRefPointer.cast(constantPool.add(romFieldRef.classRefCPIndex())).name()); + + J9ROMNameAndSignaturePointer nameAndSig = romFieldRef.nameAndSignature(); + String name = J9UTF8Helper.stringValue(nameAndSig.name()); + String desc = J9UTF8Helper.stringValue(nameAndSig.signature()); + mv.visitFieldInsn(180, owner, name, desc); + ptr += 4; + } else if ((opcode == BCNames.JBifeq) || (opcode == BCNames.JBifne) || (opcode == BCNames.JBiflt) + || (opcode == BCNames.JBifge) || (opcode == BCNames.JBifgt) || (opcode == BCNames.JBifle) + || (opcode == BCNames.JBificmpeq) || (opcode == BCNames.JBificmpne) || (opcode == BCNames.JBificmplt) + || (opcode == BCNames.JBificmpge) || (opcode == BCNames.JBificmpgt) || (opcode == BCNames.JBificmple) + || (opcode == BCNames.JBifacmpeq) || (opcode == BCNames.JBifacmpne) || (opcode == BCNames.JBgoto) + || (opcode == BCNames.JBifnull) || (opcode == BCNames.JBifnonnull)) { + mv.visitJumpInsn(opcode, labels[offset + src.getShort(ptr + 1)]); + ptr += 3; + } else if (opcode == BCNames.JBgotow) { + mv.visitJumpInsn(opcode, labels[offset + src.getInt(ptr + 1)]); + ptr += 5; + } else if (opcode == 196) { + // 196 == WIDE + // BCNames does not have a wide opcode... + // but both Walker https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/compiler/ilgen/Walker.cpp#L1494 + // and ilgen maybe expect it to exist + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/compiler/ilgen/J9ByteCode.hpp#L111 + opcode = src.getInt(ptr + 1); + if (opcode == BCNames.JBiinc) { + mv.visitIincInsn(src.getUnsignedShort(ptr + 2), src.getShort(ptr + 4)); + ptr += 6; + } else { + mv.visitVarInsn(opcode, src.getUnsignedShort(ptr + 2)); + ptr += 4; + } + } else if (opcode == BCNames.JBtableswitch) { + ptr = ptr + 4 - (offset & 3); + // reads instruction + int label = offset + src.getInt(ptr); + int min = src.getInt(ptr + 4); + int max = src.getInt(ptr + 8); + Label[] table = new Label[max - min + 1]; + ptr += 12; + for (int i = 0; i < table.length; ++i) { + table[i] = labels[offset + src.getInt(ptr)]; + ptr += 4; + } + mv.visitTableSwitchInsn(min, max, labels[label], table); + } else if (opcode == BCNames.JBlookupswitch) { + // skips 0 to 3 padding bytes + ptr = ptr + 4 - (offset & 3); + // reads instruction + int label = offset + src.getInt(ptr); + int len = src.getInt(ptr + 4); + int[] keys = new int[len]; + Label[] values = new Label[len]; + ptr += 8; + for (int i = 0; i < len; ++i) { + keys[i] = src.getInt(ptr); + values[i] = labels[offset + src.getInt(ptr + 4)]; + ptr += 8; + } + mv.visitLookupSwitchInsn(labels[label], keys, values); + } else if (opcode == BCNames.JBiloadw) { + mv.visitVarInsn(Opcodes.ILOAD, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBlloadw) { + mv.visitVarInsn(Opcodes.LLOAD, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBfloadw) { + mv.visitVarInsn(Opcodes.FLOAD, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBdloadw) { + mv.visitVarInsn(Opcodes.DLOAD, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBaloadw) { + mv.visitVarInsn(Opcodes.ALOAD, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBistorew) { + mv.visitVarInsn(Opcodes.ISTORE, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBlstorew) { + mv.visitVarInsn(Opcodes.LSTORE, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBfstorew) { + mv.visitVarInsn(Opcodes.FSTORE, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBdstorew) { + mv.visitVarInsn(Opcodes.DSTORE, src.getInt(ptr + 1)); + ptr += 3; + } else if (opcode == BCNames.JBastorew) { + mv.visitVarInsn(Opcodes.ASTORE, src.getInt(ptr + 1)); + ptr += 3; + } else if ((opcode == BCNames.JBistore) || (opcode == BCNames.JBlstore) || (opcode == BCNames.JBfstore) + || (opcode == BCNames.JBdstore) || (opcode == BCNames.JBastore) || (opcode == BCNames.JBiload) + || (opcode == BCNames.JBlload) || (opcode == BCNames.JBfload) || (opcode == BCNames.JBdload) + || (opcode == BCNames.JBaload)) { + mv.visitVarInsn(opcode, src.getByte(ptr + 1) & 0xFF); + ptr += 2; + } else if ((opcode == BCNames.JBnewarray) || (opcode == BCNames.JBbipush)) { + mv.visitIntInsn(opcode, src.getByte(ptr + 1)); + ptr += 2; + } else if (opcode == BCNames.JBsipush) { + mv.visitIntInsn(opcode, src.getShort(ptr + 1)); + ptr += 3; + } + // not sure if its most readable to handle all separate or do a immediate double check on op val + else if (opcode == BCNames.JBldc) { + + mv.visitLdcInsn(readConst(src.getByte(ptr + 1) & 0xFF, constantPool)); + ptr += 2; + } else if (opcode == BCNames.JBldcw) { + mv.visitLdcInsn(readConst(src.getShort(ptr + 1), constantPool)); + ptr += 3; + } else if (opcode == BCNames.JBldc2lw) { + + int index = src.getShort(ptr + 1); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + // since we are working with infoslots the order check is a bit ugly against the mem model, but must do + long constantvalue = (src.getByteOrder() == ByteOrder.BIG_ENDIAN) + ? ((info.slot1().longValue()) << 32) | (info.slot2().longValue() & 0xffffffffL) + : ((info.slot2().longValue()) << 32) | (info.slot1().longValue() & 0xffffffffL); + + mv.visitLdcInsn(constantvalue); + ptr += 3; + } else if (opcode == BCNames.JBldc2dw) { + + int index = src.getShort(ptr + 1); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + // since we are working with infoslots the order check is a bit ugly against the mem model, but must do + long constantvalue = (src.getByteOrder() == ByteOrder.BIG_ENDIAN) + ? ((info.slot1().longValue()) << 32) | (info.slot2().longValue() & 0xffffffffL) + : ((info.slot2().longValue()) << 32) | (info.slot1().longValue() & 0xffffffffL); + mv.visitLdcInsn(Double.longBitsToDouble(constantvalue)); + ptr += 3; + } else if ((opcode == BCNames.JBgetstatic) || (opcode == BCNames.JBputstatic) || (opcode == BCNames.JBgetfield) + || (opcode == BCNames.JBputfield)) { + + int index = src.getUnsignedShort(ptr + 1); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + J9ROMFieldRefPointer romFieldRef = J9ROMFieldRefPointer.cast(info); + String owner + = J9UTF8Helper.stringValue(J9ROMClassRefPointer.cast(constantPool.add(romFieldRef.classRefCPIndex())).name()); + + J9ROMNameAndSignaturePointer nameAndSig = romFieldRef.nameAndSignature(); + String name = J9UTF8Helper.stringValue(nameAndSig.name()); + String desc = J9UTF8Helper.stringValue(nameAndSig.signature()); + mv.visitFieldInsn(opcode, owner, name, desc); + + ptr += 3; + + // this has a strong duplication with the prev case except pointer types + // maybe can be cleaner + } else if ((opcode == BCNames.JBinvokevirtual) || (opcode == BCNames.JBinvokespecial) + || (opcode == BCNames.JBinvokestatic) || (opcode == BCNames.JBinvokeinterface)) { + + int index = src.getUnsignedShort(ptr + 1); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + + J9ROMMethodRefPointer romMethodRef = J9ROMMethodRefPointer.cast(info); + UDATA classRefCPIndex = romMethodRef.classRefCPIndex(); + J9ROMConstantPoolItemPointer cpItem = constantPool.add(classRefCPIndex); + J9ROMClassRefPointer romClassRef = J9ROMClassRefPointer.cast(cpItem); + + String owner = J9UTF8Helper.stringValue(romClassRef.name()); + + J9ROMNameAndSignaturePointer nameAndSig = romMethodRef.nameAndSignature(); + String name = J9UTF8Helper.stringValue(nameAndSig.name()); + String desc = J9UTF8Helper.stringValue(nameAndSig.signature()); + + boolean itf = (opcode == BCNames.JBinvokeinterface); + + mv.visitMethodInsn(opcode, owner, name, desc, itf); + + // if (itf) { + // ptr += 5; + // } else { + ptr += 3; + // } + } else if (opcode == BCNames.JBinvokedynamic) { + + int index = src.getShort(ptr + 1); + + long callSiteCount = pointer.callSiteCount().longValue(); + SelfRelativePointer callSiteData = SelfRelativePointer.cast(pointer.callSiteData()); + U16Pointer bsmIndices = U16Pointer.cast(callSiteData.addOffset(4 * callSiteCount)); + + J9ROMNameAndSignaturePointer invokedNameAndSig = J9ROMNameAndSignaturePointer.cast(callSiteData.add(index).get()); + String invokedName = J9UTF8Helper.stringValue(invokedNameAndSig.name()); + String invokedDesc = J9UTF8Helper.stringValue(invokedNameAndSig.signature()); + + // gets a pointer to the exact entry in the callSiteData table to the bsm data of this invoked method + long bsmIndex = bsmIndices.at(index).longValue(); // Bootstrap method index + + // get beginning of bsmData section, then the relevant entry + U16Pointer bsmDataCursor = bsmIndices.add(callSiteCount); + int currentBsmDataItem = 0; + // need to traverse to the point in the bootStrapMethodData array where this bsmIndex + // refers to, problem is that these are variable length since the contain unknown number args each + while (currentBsmDataItem != bsmIndex) { + // skip methodhandleref right off, just need to read num args + long bsmArgumentCount = bsmDataCursor.at(1).longValue(); + bsmDataCursor = bsmDataCursor.add(2 + bsmArgumentCount); + currentBsmDataItem += 1; + } + + J9ROMMethodHandleRefPointer methodHandleRef + = J9ROMMethodHandleRefPointer.cast(constantPool.add(bsmDataCursor.at(0).longValue())); + + bsmDataCursor = bsmDataCursor.add(1); + J9ROMMethodRefPointer methodRef + = J9ROMMethodRefPointer.cast(constantPool.add(methodHandleRef.methodOrFieldRefIndex().longValue())); + + // callee + J9ROMNameAndSignaturePointer nameAndSig = methodRef.nameAndSignature(); + String name = J9UTF8Helper.stringValue(nameAndSig.name()); + String desc = J9UTF8Helper.stringValue(nameAndSig.signature()); + + long bsmArgCount = bsmDataCursor.at(0).longValue(); + bsmDataCursor = bsmDataCursor.add(1); + + // map this info into the asm understanding of a handle + int methodType = methodHandleRef.handleTypeAndCpType().rightShift((int) J9DescriptionCpTypeShift).intValue(); + J9ROMClassRefPointer classRef = J9ROMClassRefPointer.cast(constantPool.add(methodRef.classRefCPIndex().longValue())); + String owner = J9UTF8Helper.stringValue(classRef.name()); + // public Handle(int tag, String owner, String name, String desc, boolean itf) + Handle bsm = new Handle(methodType, owner, name, desc, false); + Object[] bsmArgs = new Object[(int) bsmArgCount]; + + // populate the invoked method args array + for (int i = 0; i < bsmArgCount; i++) { + long argCPIndex = bsmDataCursor.at(0).longValue(); + bsmArgs[i] = readConst((int) argCPIndex, constantPool); + bsmDataCursor = bsmDataCursor.add(1); + } + + // how is providing name+desc not redundant with the bsm handle also haing those... + mv.visitInvokeDynamicInsn(invokedName, invokedDesc, bsm, bsmArgs); + ptr += 5; + } else if ((opcode == BCNames.JBnewdup) || (opcode == BCNames.JBnew) || (opcode == BCNames.JBanewarray) + || (opcode == BCNames.JBcheckcast) || (opcode == BCNames.JBinstanceof)) { + // newdup is the only openj9 specific + if (opcode == BCNames.JBnewdup) { + opcode = Opcodes.NEW; + } + int index = src.getShort(ptr + 1); + J9ROMConstantPoolItemPointer info = constantPool.add(index); + String classname = J9UTF8Helper.stringValue(J9ROMStringRefPointer.cast(info).utf8Data()); + mv.visitTypeInsn(opcode, classname); + ptr += 3; + } else if (opcode == BCNames.JBsyncReturn0) { + mv.visitInsn(Opcodes.RETURN); + ptr += 1; + } else if ((opcode == BCNames.JBsyncReturn1) || (opcode == BCNames.JBsyncReturn2)) { + opcode = getReturnType(returnType); + mv.visitInsn(opcode); + ptr += 1; + } else { + try { + mv.visitInsn(opcode); + } catch (Exception e) { + System.out.println("Encountered some not handled openj9 specific opcode: " + opcode); + } + ptr += 1; + } + } + } + + public int getReturnType(char returnType) { + + // default is void return + int opcode = 177; + + switch (returnType) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + opcode = 172; // ireturn + break; + case 'J': + opcode = 173; // lreturn + break; + case 'F': + opcode = 174; // freturn + break; + case 'D': + opcode = 175; // dreturn + break; + case 'L': + case '[': + opcode = 176; // areturn + break; + default: + break; // void + } + + return opcode; + } + + // need to find labels before everything else + public Label[] findLabels(long bytecodeSt, long bytecodeEnd, long ptr, BufferedMemorySource src) throws MemoryFault { + Label[] labels = new Label[(int) (bytecodeEnd - bytecodeSt)]; + while (ptr < bytecodeEnd) { + int offset = (int) (ptr - bytecodeSt); + int opcode = (int) (src.getByte(ptr) & 0xFF); + + if ((opcode == BCNames.JBifeq) || (opcode == BCNames.JBifne) || (opcode == BCNames.JBiflt) + || (opcode == BCNames.JBifge) || (opcode == BCNames.JBifgt) || (opcode == BCNames.JBifle) + || (opcode == BCNames.JBificmpeq) || (opcode == BCNames.JBificmpne) || (opcode == BCNames.JBificmplt) + || (opcode == BCNames.JBificmpge) || (opcode == BCNames.JBificmpgt) || (opcode == BCNames.JBificmple) + || (opcode == BCNames.JBifacmpeq) || (opcode == BCNames.JBifacmpne) || (opcode == BCNames.JBgoto) + || (opcode == BCNames.JBifnull) || (opcode == BCNames.JBifnonnull)) { + + createLabel(opcode, ptr, labels, src, bytecodeSt); + ptr += 3; + } else if (opcode == BCNames.JBgotow) { + createLabel(opcode, ptr, labels, src, bytecodeSt); + ptr += 5; + } else if (opcode == 196) { + // 196 == WIDE + // BCNames does not have a wide opcode... + // but both Walker https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/compiler/ilgen/Walker.cpp#L1494 + // and ilgen maybe expect it to exist + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/compiler/ilgen/J9ByteCode.hpp#L111 + opcode = src.getInt(ptr + 1); + if (opcode == BCNames.JBiinc) { + ptr += 6; + } else { + ptr += 4; + } + } else if ((opcode == BCNames.JBdefaultvalue) || (opcode == BCNames.JBwithfield) || (opcode == BCNames.JBiinc) + || (opcode == BCNames.JBistore) || (opcode == BCNames.JBlstore) || (opcode == BCNames.JBfstore) + || (opcode == BCNames.JBdstore) || (opcode == BCNames.JBastore) || (opcode == BCNames.JBiload) + || (opcode == BCNames.JBlload) || (opcode == BCNames.JBfload) || (opcode == BCNames.JBdload) + || (opcode == BCNames.JBaload) || (opcode == BCNames.JBnewarray) || (opcode == BCNames.JBbipush) + || (opcode == BCNames.JBldc)) { + ptr += 2; + } + + else if (opcode == BCNames.JBiincw) { + ptr += 6; + } else if (opcode == BCNames.JBmultianewarray) { + ptr += 4; + } + + else if ((opcode == BCNames.JBnewdup) || (opcode == BCNames.JBnew) || (opcode == BCNames.JBanewarray) + || (opcode == BCNames.JBcheckcast) || (opcode == BCNames.JBinstanceof) || (opcode == BCNames.JBiinc) + || (opcode == BCNames.JBnewdup) || (opcode == BCNames.JBinvokehandle) || (opcode == BCNames.JBinvokehandlegeneric) + || (opcode == BCNames.JBinvokestaticsplit) || (opcode == BCNames.JBinvokespecialsplit) + || (opcode == BCNames.JBiloadw) || (opcode == BCNames.JBistorew) || (opcode == BCNames.JBlloadw) + || (opcode == BCNames.JBlstorew) || (opcode == BCNames.JBdloadw) || (opcode == BCNames.JBfloadw) + || (opcode == BCNames.JBaloadw) || (opcode == BCNames.JBfstorew) || (opcode == BCNames.JBdstorew) + || (opcode == BCNames.JBastorew) || (opcode == BCNames.JBsipush) || (opcode == BCNames.JBldcw) + || (opcode == BCNames.JBldc2lw) || (opcode == BCNames.JBldc2dw) || (opcode == BCNames.JBgetstatic) + || (opcode == BCNames.JBputstatic) || (opcode == BCNames.JBgetfield) || (opcode == BCNames.JBputfield) + || (opcode == BCNames.JBinvokevirtual) || (opcode == BCNames.JBinvokespecial) || (opcode == BCNames.JBinvokestatic) + || (opcode == BCNames.JBinvokeinterface)) { + ptr += 3; + } else if (opcode == BCNames.JBinvokedynamic) { + ptr += 5; + } + + else if (opcode == BCNames.JBtableswitch) { + ptr = ptr + 4 - (offset & 3); + // reads the default + addLabel(labels, offset + src.getInt(ptr)); + int min = src.getInt(ptr + 4); + int max = src.getInt(ptr + 8); + Label[] table = new Label[max - min + 1]; + ptr += 12; + for (int i = 0; i < table.length; ++i) { + addLabel(labels, offset + src.getInt(ptr)); + ptr += 4; + } + } else if (opcode == BCNames.JBlookupswitch) { + // skips 0 to 3 padding bytes + ptr = ptr + 4 - (offset & 3); + // reads instruction + addLabel(labels, offset + src.getInt(ptr)); + int len = src.getInt(ptr + 4); + int[] keys = new int[len]; + Label[] values = new Label[len]; + ptr += 8; + for (int i = 0; i < len; ++i) { + addLabel(labels, offset + src.getInt(ptr + 4)); + ptr += 8; + } + } else { + ptr += 1; + } + + } + return labels; + } + + // populates a label table for us, to use later, that represent targets of jumps + public void createLabel(int opcode, long ptr, Label[] labels, BufferedMemorySource src, long methodSt) throws MemoryFault { + int offset; + if (opcode == BCNames.JBgotow) { + offset = src.getInt(ptr + 1); + } else { + offset = src.getShort(ptr + 1); + } + long target = ptr + offset; + int labelIndex = (int) (target - methodSt); + + addLabel(labels, labelIndex); + } + + public void addLabel(Label[] labels, int labelIndex) { + if (labels[labelIndex] == null) { + labels[labelIndex] = new Label(); + } + + } + + /* + * Reads a constant pool info entry + * + */ + + public Object readConst(final int index, J9ROMConstantPoolItemPointer constantPool) throws CorruptDataException { + + int HEX_RADIX = 16; + U32Pointer cpShapeDescription = J9ROMClassHelper.cpShapeDescription(pointer); + long shapeDesc = ConstantPoolHelpers.J9_CP_TYPE(cpShapeDescription, index); + BufferedMemorySource src = this.cacheMem.getMemorySource(); + + J9ROMConstantPoolItemPointer item = constantPool.add(index); + + if (shapeDesc == J9CPTYPE_CLASS) { + J9ROMClassRefPointer romClassRef = J9ROMClassRefPointer.cast(item); + return J9UTF8Helper.stringValue(romClassRef.name()); + } else if (shapeDesc == J9CPTYPE_STRING) { + J9ROMStringRefPointer romStringRef = J9ROMStringRefPointer.cast(item); + return J9UTF8Helper.stringValue(romStringRef.utf8Data()); + } else if (shapeDesc == J9CPTYPE_INT) { + J9ROMSingleSlotConstantRefPointer singleSlotConstantRef = J9ROMSingleSlotConstantRefPointer.cast(item); + return new Integer((int) singleSlotConstantRef.data().longValue()); + } else if (shapeDesc == J9CPTYPE_FLOAT) { + J9ROMSingleSlotConstantRefPointer singleSlotConstantRef = J9ROMSingleSlotConstantRefPointer.cast(item); + FloatPointer floatPtr = FloatPointer.cast(singleSlotConstantRef.dataEA()); + return new Float(floatPtr.floatAt(0)); + } else if (shapeDesc == J9CPTYPE_LONG) { + String hexValue = ""; + if (src.getByteOrder() == ByteOrder.BIG_ENDIAN) { + hexValue += item.slot1().getHexValue(); + hexValue += item.slot2().getHexValue().substring(2); + } else { + hexValue += item.slot2().getHexValue(); + hexValue += item.slot1().getHexValue().substring(2); + } + long longValue = Long.parseLong(hexValue.substring(2), HEX_RADIX); + return new Long(longValue); + } else if (shapeDesc == J9CPTYPE_DOUBLE) { + String hexValue = ""; + if (src.getByteOrder() == ByteOrder.BIG_ENDIAN) { + hexValue += item.slot1().getHexValue(); + hexValue += item.slot2().getHexValue().substring(2); + } else { + hexValue += item.slot2().getHexValue(); + hexValue += item.slot1().getHexValue().substring(2); + } + long longValue = Long.parseLong(hexValue.substring(2), HEX_RADIX); + double doubleValue = Double.longBitsToDouble(longValue); + return new Double(doubleValue); + } else if (shapeDesc == J9CPTYPE_FIELD) { + J9ROMFieldRefPointer romFieldRef = J9ROMFieldRefPointer.cast(item); + J9ROMClassRefPointer classRef = J9ROMClassRefPointer.cast(constantPool.add(romFieldRef.classRefCPIndex())); + J9ROMNameAndSignaturePointer nameAndSig = romFieldRef.nameAndSignature(); + return J9UTF8Helper.stringValue(nameAndSig.name()); + } else if ((shapeDesc == J9CPTYPE_INSTANCE_METHOD) || (shapeDesc == J9CPTYPE_STATIC_METHOD) + || (shapeDesc == J9CPTYPE_HANDLE_METHOD) || (shapeDesc == J9CPTYPE_INTERFACE_METHOD)) { + J9ROMMethodRefPointer romMethodRef = J9ROMMethodRefPointer.cast(item); + J9ROMClassRefPointer classRef = J9ROMClassRefPointer.cast(constantPool.add(romMethodRef.classRefCPIndex())); + J9ROMNameAndSignaturePointer nameAndSig = romMethodRef.nameAndSignature(); + return J9UTF8Helper.stringValue(nameAndSig.name()); + } else if (shapeDesc == J9CPTYPE_METHOD_TYPE) { + J9ROMMethodTypeRefPointer romMethodTypeRef = J9ROMMethodTypeRefPointer.cast(item); + return Type.getType(J9UTF8Helper.stringValue(J9UTF8Pointer.cast(romMethodTypeRef.signature()))); + } else if (shapeDesc == J9CPTYPE_METHODHANDLE) { + J9ROMMethodHandleRefPointer methodHandleRef = J9ROMMethodHandleRefPointer.cast(item); + J9ROMMethodRefPointer methodRef + = J9ROMMethodRefPointer.cast(constantPool.add(methodHandleRef.methodOrFieldRefIndex())); + J9ROMClassRefPointer classRef = J9ROMClassRefPointer.cast(constantPool.add(methodRef.classRefCPIndex())); + J9ROMNameAndSignaturePointer nameAndSig = methodRef.nameAndSignature(); + String owner = J9UTF8Helper.stringValue(classRef.name()); + String name = J9UTF8Helper.stringValue(nameAndSig.name()); + String signature = J9UTF8Helper.stringValue(nameAndSig.signature()); + + // for once, asm and romclass constants are fully aligned + // https://gitlab.ow2.org/asm/asm/blob/ASM_5_2/src/org/objectweb/asm/Opcodes.java#L101 + // https://github.com/eclipse/openj9/blob/v0.14.0-release/runtime/oti/j9nonbuilder.h#L2165 + long methodType = methodHandleRef.handleTypeAndCpType().rightShift((int) J9DescriptionCpTypeShift).longValue(); + + boolean itf = false; + if (methodType == Opcodes.H_INVOKEINTERFACE) { + itf = true; + } + // public Handle(int tag, String owner, String name, String desc, boolean itf) + return new Handle((int) methodType, owner, name, signature, itf); + } else { + System.out.println(" "); + return null; + } + } + +} diff --git a/src/main/java/soot/options/OptionsBase.java b/src/main/java/soot/options/OptionsBase.java index 6274038fa52..92b29321d76 100644 --- a/src/main/java/soot/options/OptionsBase.java +++ b/src/main/java/soot/options/OptionsBase.java @@ -115,6 +115,11 @@ public LinkedList classes() { return classes; } + public void addArgClass(String classname) { + classes.add(classname); + System.out.println("The classes in optionBase are: " + classes.toString()); + } + public boolean setPhaseOption(String phase, String option) { return PhaseOptions.v().processPhaseOptions(phase, option); } diff --git a/src/main/xml/options/soot_options.xml b/src/main/xml/options/soot_options.xml index d8d3fd21f5e..713c12df295 100644 --- a/src/main/xml/options/soot_options.xml +++ b/src/main/xml/options/soot_options.xml @@ -680,6 +680,26 @@ as Soot's preference for the type of source files to read when it looks for a class. + + Cache + cache + Favour shared class cache as Soot source + + Try to resolve classes first from the shared class cache found in + the Soot classpath. Fall back to .class and then .jimple files + only when unable to find a class in the cache. + + + + Only Cache Source + only-cache + Use only shared class cache as Soot source + + Try to resolve classes first from the shared class cache found in + the Soot classpath. Do not try any other types of files even when + unable to find a class in the cache. + + Class File c