opt/interdex/InterDex.h (179 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <unordered_set> #include "AssetManager.h" #include "CrossDexRefMinimizer.h" #include "CrossDexRelocator.h" #include "DexClass.h" #include "DexStructure.h" #include "InitClassesWithSideEffects.h" #include "InterDexPassPlugin.h" #include "MixedModeInfo.h" class XStoreRefs; namespace interdex { constexpr size_t MAX_DEX_NUM = 99; bool is_canary(DexClass* clazz); DexClass* create_canary(int dexnum, const DexString* store_name = nullptr); bool compare_dexclasses_for_compressed_size(DexClass* c1, DexClass* c2); class InterDex { public: InterDex(const Scope& original_scope, const DexClassesVector& dexen, AssetManager& asset_manager, ConfigFiles& conf, std::vector<std::unique_ptr<InterDexPassPlugin>>& plugins, int64_t linear_alloc_limit, bool static_prune_classes, bool normal_primary_dex, bool keep_primary_order, bool force_single_dex, bool emit_canaries, bool minimize_cross_dex_refs, bool fill_last_coldstart_dex, const cross_dex_ref_minimizer::CrossDexRefMinimizerConfig& cross_dex_refs_config, const CrossDexRelocatorConfig& cross_dex_relocator_config, size_t reserve_frefs, size_t reserve_trefs, size_t reserve_mrefs, const XStoreRefs* xstore_refs, int min_sdk, bool sort_remaining_classes, std::vector<std::string> methods_for_canary_clinit_reference, const init_classes::InitClassesWithSideEffects& init_classes_with_side_effects, bool transitively_close_interdex_order) : m_dexen(dexen), m_asset_manager(asset_manager), m_conf(conf), m_plugins(plugins), m_static_prune_classes(static_prune_classes), m_normal_primary_dex(normal_primary_dex), m_keep_primary_order(keep_primary_order), m_force_single_dex(force_single_dex), m_emit_canaries(emit_canaries), m_minimize_cross_dex_refs(minimize_cross_dex_refs), m_fill_last_coldstart_dex(fill_last_coldstart_dex), m_emitting_scroll_set(false), m_emitting_bg_set(false), m_emitted_bg_set(false), m_emitting_extended(false), m_cross_dex_ref_minimizer(cross_dex_refs_config), m_cross_dex_relocator_config(cross_dex_relocator_config), m_original_scope(original_scope), m_scope(build_class_scope(m_dexen)), m_xstore_refs(xstore_refs), m_sort_remaining_classes(sort_remaining_classes), m_methods_for_canary_clinit_reference( std::move(methods_for_canary_clinit_reference)), m_transitively_close_interdex_order(transitively_close_interdex_order) { m_dexes_structure.set_linear_alloc_limit(linear_alloc_limit); m_dexes_structure.set_reserve_frefs(reserve_frefs); m_dexes_structure.set_reserve_trefs(reserve_trefs); m_dexes_structure.set_reserve_mrefs(reserve_mrefs); m_dexes_structure.set_min_sdk(min_sdk); m_dexes_structure.set_init_classes_with_side_effects( &init_classes_with_side_effects); load_interdex_types(); } ~InterDex() { delete m_cross_dex_relocator; } size_t get_num_cold_start_set_dexes() const { return m_dexes_structure.get_num_coldstart_dexes(); } size_t get_num_scroll_dexes() const { return m_dexes_structure.get_num_scroll_dexes(); } const cross_dex_ref_minimizer::CrossDexRefMinimizerStats& get_cross_dex_ref_minimizer_stats() const { return m_cross_dex_ref_minimizer.stats(); } CrossDexRelocatorStats get_cross_dex_relocator_stats() const { if (m_cross_dex_relocator != nullptr) { return m_cross_dex_relocator->stats(); } return CrossDexRelocatorStats(); } /** * Only call this if you know what you are doing. * This will leave the current instance is in an unusable state. */ DexClassesVector take_outdex() { return std::move(m_outdex); } void run(); void run_on_nonroot_store(); void add_dexes_from_store(const DexStore& store); void cleanup(const Scope& final_scope); const std::vector<DexType*>& get_interdex_types() const { return m_interdex_types; } size_t get_current_classes_when_emitting_remaining() const { return m_current_classes_when_emitting_remaining; } size_t get_transitive_closure_added() const { return m_transitive_closure_added; } size_t get_transitive_closure_moved() const { return m_transitive_closure_moved; } private: void run_in_force_single_dex_mode(); bool should_not_relocate_methods_of_class(const DexClass* clazz); void add_to_scope(DexClass* cls); bool should_skip_class_due_to_plugin(DexClass* clazz); struct EmitResult { bool emitted{false}; bool overflowed{false}; operator bool() const { return emitted; } }; EmitResult emit_class(DexInfo& dex_info, DexClass* clazz, bool check_if_skip, bool perf_sensitive, DexClass** canary_cls, bool* overflowed = nullptr); void emit_primary_dex( const DexClasses& primary_dex, const std::vector<DexType*>& interdex_order, const std::unordered_set<DexClass*>& unreferenced_classes); void emit_interdex_classes( DexInfo& dex_info, const std::vector<DexType*>& interdex_types, const std::unordered_set<DexClass*>& unreferenced_classes, DexClass** canary_cls); void init_cross_dex_ref_minimizer_and_relocate_methods(); void emit_remaining_classes(DexInfo& dex_info, DexClass** canary_cls); DexClass* get_canary_cls(DexInfo& dex_info); void flush_out_dex(DexInfo& dex_info, DexClass* canary_cls); void set_clinit_methods_if_needed(DexClass* cls); /** * Stores in m_interdex_order a list of coldstart types. It will only contain: * * classes that still exist in the current scope * * + a "fake" type for each of the class markers (ex: DexEndMarker etc) */ void load_interdex_types(); /** * Makes sure that classes in the dex end up in the interdex list. * For the classes that aren't already in the list, it adds them at * the beginning. */ void update_interdexorder(const DexClasses& dex, std::vector<DexType*>* interdex_types); const DexClassesVector& m_dexen; DexClassesVector m_outdex; AssetManager& m_asset_manager; ConfigFiles& m_conf; std::vector<std::unique_ptr<InterDexPassPlugin>>& m_plugins; // TODO: Encapsulate (primary|all) dex flags under one config. bool m_static_prune_classes; bool m_normal_primary_dex; bool m_keep_primary_order; bool m_force_single_dex; bool m_emit_canaries; bool m_minimize_cross_dex_refs; bool m_fill_last_coldstart_dex; bool m_emitting_scroll_set; bool m_emitting_bg_set; bool m_emitted_bg_set; bool m_emitting_extended; std::vector<std::tuple<std::string, DexInfo>> m_dex_infos; DexesStructure m_dexes_structure; std::vector<DexType*> m_end_markers; std::vector<DexType*> m_scroll_markers; cross_dex_ref_minimizer::CrossDexRefMinimizer m_cross_dex_ref_minimizer; const CrossDexRelocatorConfig m_cross_dex_relocator_config; const Scope& m_original_scope; CrossDexRelocator* m_cross_dex_relocator{nullptr}; Scope m_scope; std::vector<DexType*> m_interdex_types; const XStoreRefs* m_xstore_refs; bool m_sort_remaining_classes; size_t m_current_classes_when_emitting_remaining{0}; std::vector<std::string> m_methods_for_canary_clinit_reference; size_t m_transitive_closure_added{0}; size_t m_transitive_closure_moved{0}; const bool m_transitively_close_interdex_order; }; } // namespace interdex