Example #1
0
  public boolean mustCompile(Clazz clazz) {
    File oFile = config.getOFile(clazz);
    if (!oFile.exists() || oFile.lastModified() < clazz.lastModified()) {
      return true;
    }

    Set<Dependency> dependencies = clazz.getDependencies();
    for (Dependency dep : dependencies) {
      Clazz depClazz = config.getClazzes().load(dep.getClassName());
      if (depClazz == null) {
        if (dep.getPath() != null) {
          // depClazz was available the last time clazz was compiled but is now gone
          return true;
        }
      } else {
        if (dep.getPath() == null) {
          // depClazz was not available the last time clazz was compiled but is now available
          return true;
        }
        if (!dep.getPath().equals(depClazz.getPath().getFile().getAbsolutePath())) {
          // depClazz was located in another place the last time clazz was built
          return true;
        }
        if (depClazz.isInBootClasspath() != dep.isInBootClasspath()) {
          // depClazz has moved to/from the bootclasspath since the last time clazz was built
          return true;
        }
        if (depClazz.lastModified() > oFile.lastModified()) {
          // depClazz has been changed since the last time clazz was built
          return true;
        }
      }
    }

    // No class or interface has zero dependencies (we always add java.lang.Object as a dependency)
    // If dependencies is empty it probably means that an error occurred while reading the
    // serialized dependencies. By returning true here in that case the class will be recompiled
    // and the dependencies regenerated.
    return dependencies.isEmpty();
  }
Example #2
0
  public void compile(Clazz clazz) throws IOException {
    reset();

    File llFile = config.getLlFile(clazz);
    File bcFile = config.getBcFile(clazz);
    File sFile = config.getSFile(clazz);
    File oFile = config.getOFile(clazz);
    llFile.getParentFile().mkdirs();
    bcFile.getParentFile().mkdirs();
    sFile.getParentFile().mkdirs();
    oFile.getParentFile().mkdirs();

    OutputStream out = null;
    try {
      config.getLogger().debug("Compiling %s", clazz);
      out = new FileOutputStream(llFile);
      compile(clazz, out);
    } catch (Throwable t) {
      FileUtils.deleteQuietly(llFile);
      if (t instanceof IOException) {
        throw (IOException) t;
      }
      if (t instanceof RuntimeException) {
        throw (RuntimeException) t;
      }
      throw new RuntimeException(t);
    } finally {
      IOUtils.closeQuietly(out);
    }

    config.getLogger().debug("Optimizing %s", clazz);
    CompilerUtil.opt(config, llFile, bcFile, "-mem2reg", "-always-inline");

    config.getLogger().debug("Generating %s assembly for %s", config.getArch(), clazz);
    CompilerUtil.llc(config, bcFile, sFile);

    patchAsmWithFunctionSizes(clazz, sFile);

    config.getLogger().debug("Assembling %s", clazz);
    CompilerUtil.assemble(config, sFile, oFile);
  }
Example #3
0
  private StructureConstant createClassInfoErrorStruct() {
    /*
     * Check that clazz can be loaded, i.e. that the superclass
     * and interfaces of the class exist and are accessible to the
     * class. Also check that any exception the class uses in catch
     * clauses exist and is accessible to the class. If the class
     * cannot be loaded we override the ClassInfoHeader struct
     * produced by the ClassCompiler for the class with one which
     * tells the code in bc.c to throw an appropriate exception
     * whenever clazz is accessed.
     */

    int errorType = ClassCompiler.CI_ERROR_TYPE_NONE;
    String errorMessage = null;
    if (!sootClass.isInterface() && sootClass.hasSuperclass()) {
      // Check superclass
      SootClass superclazz = sootClass.getSuperclass();
      if (superclazz.isPhantom()) {
        errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
        errorMessage = superclazz.getName();
      } else if (!checkClassAccessible(superclazz, sootClass)) {
        errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
        errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, superclazz, sootClass);
      } else if (superclazz.isInterface()) {
        errorType = ClassCompiler.CI_ERROR_TYPE_INCOMPATIBLE_CLASS_CHANGE;
        errorMessage =
            String.format("class %s has interface %s as super class", sootClass, superclazz);
      }
      // No need to check for ClassCircularityError. Soot doesn't handle
      // such problems so the compilation will fail earlier.
    }

    if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
      // Check interfaces
      for (SootClass interfaze : sootClass.getInterfaces()) {
        if (interfaze.isPhantom()) {
          errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
          errorMessage = interfaze.getName();
          break;
        } else if (!checkClassAccessible(interfaze, sootClass)) {
          errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
          errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, interfaze, sootClass);
          break;
        } else if (!interfaze.isInterface()) {
          errorType = ClassCompiler.CI_ERROR_TYPE_INCOMPATIBLE_CLASS_CHANGE;
          errorMessage =
              String.format(
                  "class %s tries to implement class %s as interface", sootClass, interfaze);
          break;
        }
      }
    }

    if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
      // Check exceptions used in catch clauses. I cannot find any info in
      // the VM spec specifying that this has to be done when the class is loaded.
      // However, this is how it's done in other VMs so we do it too.
      for (String exName : catches) {
        Clazz ex = config.getClazzes().load(exName);
        if (ex == null || ex.getSootClass().isPhantom()) {
          errorType = ClassCompiler.CI_ERROR_TYPE_NO_CLASS_DEF_FOUND;
          errorMessage = exName;
          break;
        } else if (!checkClassAccessible(ex.getSootClass(), sootClass)) {
          errorType = ClassCompiler.CI_ERROR_TYPE_ILLEGAL_ACCESS;
          errorMessage = String.format(ILLEGAL_ACCESS_ERROR_CLASS, ex, sootClass);
          break;
        }
      }
    }

    if (errorType == ClassCompiler.CI_ERROR_TYPE_NONE) {
      return null;
    }

    // Create a ClassInfoError struct
    StructureConstantBuilder error = new StructureConstantBuilder();
    error.add(new NullConstant(I8_PTR)); // Points to the runtime Class struct
    error.add(new IntegerConstant(ClassCompiler.CI_ERROR));
    error.add(getString(getInternalName(sootClass)));
    error.add(new IntegerConstant(errorType));
    error.add(getString(errorMessage));
    return error.build();
  }
Example #4
0
  private void compile(Clazz clazz, OutputStream out) throws IOException {
    sootClass = clazz.getSootClass();
    mb = new ModuleBuilder();
    trampolines = new HashSet<Trampoline>();
    catches = new HashSet<String>();
    classFields = getClassFields(sootClass);
    instanceFields = getInstanceFields(sootClass);
    classType = getClassType(sootClass);
    instanceType = getInstanceType(sootClass);

    attributesEncoder.encode(mb, sootClass);

    // Add a <clinit> method if the class has ConstantValueTags but no <clinit>.
    // This has to be done before createInfoStruct() is called otherwise the
    // ClassInfoHeader->initializer value will become NULL and constant static fields
    // will never be initialized.
    if (!sootClass.declaresMethodByName("<clinit>") && hasConstantValueTags(classFields)) {
      SootMethod clinit =
          new SootMethod("<clinit>", Collections.EMPTY_LIST, VoidType.v(), Modifier.STATIC);
      JimpleBody body = Jimple.v().newBody(clinit);
      clinit.setActiveBody(body);
      body.getUnits().add(new JReturnVoidStmt());
      this.sootClass.addMethod(clinit);
    }

    if (isStruct(sootClass)) {
      if (!Modifier.isFinal(sootClass.getModifiers())) {
        throw new IllegalArgumentException("Struct class must be final");
      }
      SootMethod _sizeOf =
          new SootMethod("_sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PROTECTED);
      sootClass.addMethod(_sizeOf);
      SootMethod sizeOf =
          new SootMethod(
              "sizeOf", Collections.EMPTY_LIST, IntType.v(), Modifier.PUBLIC | Modifier.STATIC);
      sootClass.addMethod(sizeOf);
    }

    mb.addInclude(
        getClass()
            .getClassLoader()
            .getResource(
                String.format("header-%s-%s.ll", config.getOs().getFamily(), config.getArch())));
    mb.addInclude(getClass().getClassLoader().getResource("header.ll"));

    mb.addFunction(createInstanceof());
    mb.addFunction(createCheckcast());
    mb.addFunction(createLdcClass());
    mb.addFunction(createLdcClassWrapper());
    Function allocator = createAllocator();
    mb.addFunction(allocator);
    mb.addFunction(createClassInitWrapperFunction(allocator.ref()));

    for (SootField f : sootClass.getFields()) {
      Function getter = createFieldGetter(f);
      Function setter = createFieldSetter(f);
      mb.addFunction(getter);
      mb.addFunction(setter);
      if (f.isStatic() && !f.isPrivate()) {
        mb.addFunction(createClassInitWrapperFunction(getter.ref()));
        if (!f.isFinal()) {
          mb.addFunction(createClassInitWrapperFunction(setter.ref()));
        }
      }
    }

    for (SootMethod method : sootClass.getMethods()) {
      String name = method.getName();
      if (isBridge(method)) {
        bridgeMethod(method);
      } else if (isStruct(sootClass)
          && ("_sizeOf".equals(name) || "sizeOf".equals(name) || isStructMember(method))) {
        structMember(method);
      } else if (method.isNative()) {
        nativeMethod(method);
      } else if (!method.isAbstract()) {
        method(method);
      }
      if (!name.equals("<clinit>")
          && !name.equals("<init>")
          && !method.isPrivate()
          && !method.isStatic()
          && !Modifier.isFinal(method.getModifiers())
          && !Modifier.isFinal(sootClass.getModifiers())) {

        createLookupFunction(method);
      }
      if (method.isStatic()) {
        String fnName = mangleMethod(method);
        if (method.isSynchronized()) {
          fnName += "_synchronized";
        }
        FunctionRef fn = new FunctionRef(fnName, getFunctionType(method));
        mb.addFunction(createClassInitWrapperFunction(fn));
      }
    }

    Set<String> trampolineDependencies = new HashSet<String>();
    for (Trampoline trampoline : trampolines) {
      trampolineResolver.compile(mb, trampoline);
      trampolineDependencies.addAll(trampolineResolver.getDependencies());
    }

    Global classInfoStruct = null;
    StructureConstant classInfoErrorStruct = createClassInfoErrorStruct();
    if (classInfoErrorStruct != null) {
      // The class cannot be loaded at runtime. Replace the ClassInfo struct
      // with a ClassInfoError struct with details of why.
      classInfoStruct = new Global(mangleClass(sootClass) + "_info_struct", classInfoErrorStruct);
    } else {
      classInfoStruct =
          new Global(mangleClass(sootClass) + "_info_struct", createClassInfoStruct());
    }
    mb.addGlobal(classInfoStruct);

    Function infoFn = FunctionBuilder.infoStruct(sootClass);
    infoFn.add(new Ret(new ConstantBitcast(classInfoStruct.ref(), I8_PTR_PTR)));
    mb.addFunction(infoFn);

    out.write(mb.build().toString().getBytes("UTF-8"));

    clazz.clearDependencies();
    clazz.addDependency(
        "java/lang/Object"); // Make sure no class or interface has zero dependencies
    if (sootClass.hasSuperclass() && !sootClass.isInterface()) {
      clazz.addDependency(getInternalName(sootClass.getSuperclass()));
    }
    for (SootClass iface : sootClass.getInterfaces()) {
      clazz.addDependency(getInternalName(iface));
    }
    for (SootField f : sootClass.getFields()) {
      addDependencyIfNeeded(clazz, f.getType());
    }
    for (SootMethod m : sootClass.getMethods()) {
      addDependencyIfNeeded(clazz, m.getReturnType());
      @SuppressWarnings("unchecked")
      List<soot.Type> paramTypes = (List<soot.Type>) m.getParameterTypes();
      for (soot.Type type : paramTypes) {
        addDependencyIfNeeded(clazz, type);
      }
    }
    clazz.addDependencies(attributesEncoder.getDependencies());
    clazz.addDependencies(trampolineDependencies);
    clazz.addDependencies(catches);

    for (Trampoline t : trampolines) {
      if (!(t instanceof LdcString)) {
        String desc = t.getTarget();
        if (desc.charAt(0) == 'L' || desc.charAt(0) == '[') {
          // Target is a descriptor
          addDependencyIfNeeded(clazz, desc);
        } else {
          clazz.addDependency(t.getTarget());
        }
      }
      if (t instanceof FieldAccessor) {
        addDependencyIfNeeded(clazz, ((FieldAccessor) t).getFieldDesc());
      } else if (t instanceof Invoke) {
        String methodDesc = ((Invoke) t).getMethodDesc();
        addDependencyIfNeeded(clazz, getReturnTypeDescriptor(methodDesc));
        for (String desc : getParameterDescriptors(methodDesc)) {
          addDependencyIfNeeded(clazz, desc);
        }
      }
    }
    clazz.saveDependencies();
  }
Example #5
0
  private void patchAsmWithFunctionSizes(Clazz clazz, File sFile) throws IOException {
    Set<String> functionNames = new HashSet<String>();
    for (SootMethod method : clazz.getSootClass().getMethods()) {
      if (!method.isAbstract()) {
        String name = mangleMethod(method);
        if (config.getOs().getFamily() == OS.Family.darwin) {
          name = "_" + name;
        }
        functionNames.add(name);
      }
    }

    String localLabelPrefix = ".L";
    String prefix = mangleClass(clazz.getInternalName());
    if (config.getOs().getFamily() == OS.Family.darwin) {
      localLabelPrefix = "L";
      prefix = "_" + prefix;
    }
    String infoStructLabel = prefix + "_info_struct";
    Pattern methodImplPattern =
        Pattern.compile("\\s*\\.(?:quad|long)\\s+(" + Pattern.quote(prefix) + "[^\\s]+).*");

    File outFile = new File(sFile.getParentFile(), sFile.getName() + ".tmp");

    BufferedReader in = null;
    BufferedWriter out = null;
    try {
      in = new BufferedReader(new InputStreamReader(new FileInputStream(sFile), "UTF-8"));
      out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
      String line = null;
      String currentFunction = null;
      while ((line = in.readLine()) != null) {
        if (currentFunction == null) {
          out.write(line);
          out.write('\n');
          if (line.startsWith(prefix)) {
            int colon = line.indexOf(':');
            if (colon == -1) {
              continue;
            }
            String label = line.substring(0, colon);
            if (functionNames.contains(label)) {
              currentFunction = label;
            } else if (label.equals(infoStructLabel)) {
              break;
            }
          }
        } else if (line.trim().equals(".cfi_endproc")
            || line.trim().startsWith(".section")
            || line.trim().startsWith(".globl")) {
          out.write(localLabelPrefix);
          out.write(currentFunction);
          out.write("_end:\n\n");
          currentFunction = null;
          out.write(line);
          out.write('\n');
        } else {
          out.write(line);
          out.write('\n');
        }
      }

      while ((line = in.readLine()) != null) {
        out.write(line);
        out.write('\n');
        if (line.contains(prefix)) {
          Matcher matcher = methodImplPattern.matcher(line);
          if (matcher.matches()) {
            String functionName = matcher.group(1);
            if (functionNames.contains(functionName)) {
              line = in.readLine();
              if (line.contains(String.valueOf(DUMMY_METHOD_SIZE))) {
                out.write("\t.long\t");
                out.write(localLabelPrefix + functionName + "_end - " + functionName);
                out.write('\n');
              } else {
                out.write(line);
                out.write('\n');
              }
            }
          }
        }
      }
    } finally {
      IOUtils.closeQuietly(in);
      IOUtils.closeQuietly(out);
    }

    sFile.renameTo(new File(sFile.getParentFile(), sFile.getName() + ".orig"));
    outFile.renameTo(sFile);
  }