void UnterfacePass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) { Scope scope = build_class_scope(dexen); InterfaceImplementations interfaces(scope); assert(interfaces.print_all()); auto one_level = exclude(interfaces, HAS_SUPER | HAS_CHILDREN | NO_VMETHODS, IMPL_MULTIPLE_INTERFACES | HAS_SUPER | HAS_CHILDREN | IS_ABSTRACT | NO_MATCH_INTERFACE_METHODS | HAS_MULTIPLE_INSTANCE_FIELDS | MULTIPLE_ARGS_CTOR | HAS_CLINIT | NO_VMETHODS | HAS_STATIC_FIELDS | HAS_DIRECT_METHODS); interfaces.analyze_candidates(one_level, "No hierarchy, perfect match"); // optimize std::vector<DexClass*> untfs; std::unordered_set<DexClass*> removed; //optimize(scope, candidates, untfs, removed); // write back DexClassesVector outdex; DexClasses& orig_classes = dexen[0]; DexClasses classes(orig_classes.size() + untfs.size() - removed.size()); int pos = 0; for (int i = 0; i < orig_classes.size(); ++i) { auto cls = orig_classes.get(i); if (removed.find(cls) == removed.end()) { classes.insert_at(cls, pos++); } } for (auto untf : untfs) { classes.insert_at(untf, pos++); } outdex.emplace_back(std::move(classes)); for (size_t i = 1; i < dexen.size(); i++) { outdex.emplace_back(std::move(dexen[i])); } dexen = std::move(outdex); }
void InterDexPass::run_pass(DexClassesVector& dexen, PgoFiles& pgo) { bool static_prune = false; if (m_config["static_prune"] != nullptr) { auto prune_str = m_config["static_prune"].asString().toStdString(); if (prune_str == "1") { static_prune = true; } } auto first_attempt = run_interdex(dexen, pgo, true, static_prune); if (first_attempt.size() > dexen.size()) { fprintf(stderr, "Warning, Interdex grew the number of dexes from %lu to %lu! \n \ Retrying without cutting off interdex dexes. \n", dexen.size(), first_attempt.size()); dexen = run_interdex(dexen, pgo, false, static_prune); } else {
void StaticSinkPass::run_pass(DexClassesVector& dexen, ConfigFiles& cfg) { auto method_list = cfg.get_coldstart_methods(); auto methods = strings_to_dexmethods(method_list); TRACE(SINK, 1, "methods used in coldstart: %lu\n", methods.size()); auto coldstart_classes = get_coldstart_classes(dexen, cfg); count_coldstart_statics(coldstart_classes); auto statics = get_noncoldstart_statics(coldstart_classes, methods); TRACE(SINK, 1, "statics not used in coldstart: %lu\n", statics.size()); remove_primary_dex_refs(dexen[0], statics); TRACE(SINK, 1, "statics after removing primary dex: %lu\n", statics.size()); auto sink_map = get_sink_map(dexen, coldstart_classes, statics); TRACE(SINK, 1, "statics with sinkable callsite: %lu\n", sink_map.size()); auto holder = move_statics_out(statics, sink_map); TRACE(SINK, 1, "methods in static holder: %lu\n", holder->get_dmethods().size()); DexClasses dc(1); dc.insert_at(holder, 0); dexen.emplace_back(std::move(dc)); }
int main(int argc, char* argv[]) { signal(SIGSEGV, crash_backtrace); signal(SIGABRT, crash_backtrace); signal(SIGBUS, crash_backtrace); g_redex = new RedexContext(); auto passes = create_passes(); Arguments args; std::vector<KeepRule> rules; // Currently there are two sources that specify the library jars: // 1. The jar_path argument, which may specify one library jar. // 2. The library_jars vector, which lists the library jars specified in // the ProGuard configuration. // If -jarpath specified a library jar it is appended to the // library_jars vector so this vector can be used to iterate over // all the library jars regardless of whether they were specified // on the command line or ProGuard file. // TODO: Make the command line -jarpath option like a colon separated // list of library JARS. std::set<std::string> library_jars; auto start = parse_args(argc, argv, args); if (!dir_is_writable(args.out_dir)) { fprintf(stderr, "outdir %s is not a writable directory\n", args.out_dir.c_str()); exit(1); } if (!args.proguard_config.empty()) { if (!load_proguard_config_file( args.proguard_config.c_str(), &rules, &library_jars)) { fprintf(stderr, "ERROR: Unable to open proguard config %s\n", args.proguard_config.c_str()); // For now tolerate missing or unparseable ProGuard configuration files. // start = 0; } } else { TRACE(MAIN, 1, "Skipping parsing the proguard config file " "because no file was specified\n"); } for (const auto jar_path : args.jar_paths) { std::stringstream jar_stream(jar_path); std::string dependent_jar_path; while (std::getline(jar_stream, dependent_jar_path, ':')) { TRACE(MAIN, 2, "Dependent JAR specified on command-line: %s\n", dependent_jar_path.c_str()); library_jars.emplace(dependent_jar_path); } } if (start == 0 || start == argc) { usage(); exit(1); } DexClassesVector dexen; for (int i = start; i < argc; i++) { dexen.emplace_back(load_classes_from_dex(argv[i])); } for (const auto& library_jar : library_jars) { TRACE(MAIN, 1, "LIBRARY JAR: %s\n", library_jar.c_str()); if (!load_jar_file(library_jar.c_str())) { fprintf( stderr, "WARNING: Error in jar %s - continue. This may lead to unexpected " "behavior, please check your jars\n", library_jar.c_str()); } } ConfigFiles cfg(args.config); cfg.using_seeds = false; if (!args.seeds_filename.empty()) { cfg.using_seeds = init_seed_classes(args.seeds_filename) > 0; } PassManager manager(passes, rules, args.config); manager.run_passes(dexen, cfg); TRACE(MAIN, 1, "Writing out new DexClasses...\n"); LocatorIndex* locator_index = nullptr; if (args.config.get("emit_locator_strings", false).asBool()) { TRACE(LOC, 1, "Will emit class-locator strings for classloader optimization\n"); locator_index = new LocatorIndex(make_locator_index(dexen)); } dex_output_stats_t totals; std::vector<dex_output_stats_t> dexes_stats; auto pos_output = args.config.get("line_number_map", "").asString(); std::unique_ptr<PositionMapper> pos_mapper(PositionMapper::make(pos_output)); for (size_t i = 0; i < dexen.size(); i++) { std::stringstream ss; ss << args.out_dir + "/classes"; if (i > 0) { ss << (i + 1); } ss << ".dex"; auto stats = write_classes_to_dex( ss.str(), &dexen[i], locator_index, i, cfg, args.config, pos_mapper.get()); totals += stats; dexes_stats.push_back(stats); } auto stats_output = args.config.get("stats_output", "").asString(); auto method_move_map = args.config.get("method_move_map", "").asString(); pos_mapper->write_map(); output_stats(stats_output.c_str(), totals, dexes_stats, manager); output_moved_methods_map(method_move_map.c_str(), dexen, cfg); print_warning_summary(); delete g_redex; TRACE(MAIN, 1, "Done.\n"); return 0; }