From 609e6c69cdfd0df11d29d185d1e4784495f356a6 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Thu, 21 Mar 2024 14:20:44 +0100 Subject: [PATCH] wip --- androscalpel/src/dex_fragment.rs | 264 ++- androscalpel/src/dex_writer.rs | 3315 +----------------------------- 2 files changed, 336 insertions(+), 3243 deletions(-) diff --git a/androscalpel/src/dex_fragment.rs b/androscalpel/src/dex_fragment.rs index 27bc792..aaea8db 100644 --- a/androscalpel/src/dex_fragment.rs +++ b/androscalpel/src/dex_fragment.rs @@ -45,12 +45,6 @@ pub struct DexFragment { class_def: ClassDefItem, /// The class_data. class_data: Option, - // TODO: type list should be handle like other ids - // TODO: type list inside of proto ids are not handled here - // /// The type lists found in the classes associated to their index in the type_lists section. - // type_lists_index: HashMap, - // /// The type_lists section and the offset of the lists inside the section. - // type_lists_with_offset: Vec<(TypeList, u32)>, /// The encoded_array_items section. encoded_array_items: Vec, /// The annotations_directory_item. @@ -69,9 +63,40 @@ pub struct DexFragment { debug_info_items: Vec, /// The list of interfaces of the class. interfaces: Vec, + /// The current link state of the fragment. + link_state: FragLinkState, } impl DexFragment { + /// The strings in the dex fragment, sorted. + pub fn strings(&self) -> &[DexString] { + &self.strings + } + /// The types in the dex fragment, sorted. + pub fn type_ids(&self) -> &[IdType] { + &self.type_ids + } + /// The prototypes in the dex fragment, sorted. + pub fn proto_ids(&self) -> &[IdMethodType] { + &self.proto_ids + } + /// The field ids in the dex fragment, sorted. + pub fn field_ids(&self) -> &[IdField] { + &self.field_ids + } + /// The methods ids in the dex fragment, sorted. + pub fn method_ids(&self) -> &[IdMethod] { + &self.method_ids + } + /// The list of interfaces of the class. + pub fn interfaces(&self) -> &[IdType] { + &self.interfaces + } + /// The method_handles section. + pub fn method_handles(&self) -> &[MethodHandleItem] { + &self.method_handles + } + pub fn new(class: &Class) -> Result { debug!( "Building dex fragment for class {}", @@ -108,6 +133,7 @@ impl DexFragment { annotation_set_lists: vec![], debug_info_items: vec![], interfaces: class.interfaces.clone(), + link_state: FragLinkState::Unlinked, }; frag.strings = class.get_all_strings().into_iter().collect(); frag.strings.sort(); @@ -1852,6 +1878,168 @@ impl DexFragment { self.annotation_set_lists.push(list); Ok(()) } + + /// Some structure change size depending of the value of the index they refere to. + /// The new size needs to be known before the offset can be linked, so the idx are + /// linked before. + pub fn link_global_ids( + &mut self, + global_strings: &[DexString], + global_type_ids: &[IdType], + global_proto_ids: &[IdMethodType], + global_field_ids: &[IdField], + global_method_ids: &[IdMethod], + nb_method_handle_before_fragment: usize, + ) -> Result<()> { + self.link_state.start_linking_idx()?; + let string_reindex = Vec::with_capacity(self.strings.len()); + let mut global_idx = 0; + for s in self.strings { + while global_idx < global_strings.len() && global_strings[global_idx] != s { + global_idx += 1; + } + if global_idx == global_strings.len() { + bail!("String {} not found in global index", s.__str__()); + } + string_reindex.push(global_idx as u32); + } + let type_reindex = Vec::with_capacity(self.type_ids.len()); + let mut global_idx = 0; + for ty in self.type_ids { + while global_idx < global_type_ids.len() && global_type_ids[global_idx] != ty { + global_idx += 1; + } + if global_idx == global_type_ids.len() { + bail!("Type {} not found in global index", ty.__str__()); + } + type_reindex.push(global_idx as u32); + } + let proto_reindex = Vec::with_capacity(self.proto_ids.len()); + let mut global_idx = 0; + for proto in self.proto_ids { + while global_idx < global_proto_ids.len() && global_proto_ids[global_idx] != proto { + global_idx += 1; + } + if global_idx == global_proto_ids.len() { + bail!("Prototype {} not found in global index", proto.__str__()); + } + proto_reindex.push(global_idx as u32); + } + let field_reindex = Vec::with_capacity(self.field_ids.len()); + let mut global_idx = 0; + for field in self.field_ids { + while global_idx < global_field_ids.len() && global_field_ids[global_idx] != field { + global_idx += 1; + } + if global_idx == global_field_ids.len() { + bail!("Field {} not found in global index", field.__str__()); + } + field_reindex.push(global_idx as u16); + } + let method_reindex = Vec::with_capacity(self.method_ids.len()); + let mut global_idx = 0; + for meth in self.method_ids { + while global_idx < global_method_ids.len() && global_method_ids[global_idx] != meth { + global_idx += 1; + } + if global_idx == global_method_ids.len() { + bail!("Method {} not found in global index", meth.__str__()); + } + method_reindex.push(global_idx as u16); + } + + self.link_id_class_def(&string_reindex, &type_reindex); + self.link_id_method_handle(&field_reindex, &method_reindex); + + todo!() + } + + fn link_id_class_def(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { + self.class_def.class_idx = type_reindex[self.class_def.class_idx as usize]; + self.class_def.superclass_idx = type_reindex[self.class_def.superclass_idx as usize]; + self.class_def.source_file_idx = string_reindex[self.class_def.source_file_idx as usize]; + } + + fn link_id_method_handle(&mut self, field_reindex: &[u16], method_reindex: &[u16]) { + for handle in self.method_handles { + let reindexer = match handle.method_handle_type { + MethodHandleType::StaticPut => field_reindex, + MethodHandleType::StaticGet => field_reindex, + MethodHandleType::InstancePut => field_reindex, + MethodHandleType::InstanceGet => field_reindex, + MethodHandleType::InvokeStatic => method_reindex, + MethodHandleType::InvokeInstance => method_reindex, + MethodHandleType::InvokeConstructor => method_reindex, + MethodHandleType::InvokeDirect => method_reindex, + MethodHandleType::InvokeInterface => method_reindex, + }; + handle.field_or_method_id = reindexer[handle.field_or_method_id as usize]; + } + } + + fn link_id_code(&mut self, string_reindex: &[u32], type_reindex: &[u32]) { + let mut total_size = 0; + let mut code_item_relocation = if let FragLinkState::LinkedIdx { + code_item_relocation, + .. + } = self.link_state + { + code_item_relocation + } else { + // link_global_ids() should prevent that + panic!("link_id_code should not be run outside of fn link_global_ids(..)"); + }; + for code in self.code_items { + let current_size = code.size(); + for ins in &mut code.insns { + Self::link_id_ins(ins, string_reindex); + } + // TODO: TryItem recompute handler_off + if let Some(handlers) = code.handlers { + let mut handler_off_reindex = HashMap::new(); + let mut current_offset = handlers.size_field().size(); + let mut old_offset = handlers.size_field().size(); + for handlers in handlers.list { + handler_off_reindex.insert(old_offset as u16, current_offset as u16); + old_offset += handlers.size(); + for handler in handlers.handlers { + handler.type_idx.0 = type_reindex[handler.type_idx.0 as usize]; + } + current_offset += handlers.size(); + } + for try_ in code.tries { + try_.handler_off = *handler_off_reindex + .get(&try_.handler_off) + .expect("Something whent wrong with the handle reindexing"); + } + } + } + } + fn link_id_ins(ins: &mut InsFormat, string_reindex: &[u32]) { + match ins { + InsFormat::Format31C { op: 0x1b, b, .. } => *b = string_reindex[b as usize], + InsFormat::Format21C { op: 0x1a, b, .. } => todo!(), // TODO FUCK this + _ => todo!(), + } + } + fn link_id_string_data(&mut self) { + todo!() + } + fn link_id_debug_info(&mut self) { + todo!() + } + fn link_id_annotation(&mut self) { + todo!() + } + fn link_id_encoded_array(&mut self) { + todo!() + } + fn link_id_annotation_dir(&mut self) { + todo!() + } + fn link_id_class_data(&mut self) { + todo!() + } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -1859,7 +2047,6 @@ enum FragSection { ClassDefItem, CallSiteIdItem, MethodHandleItem, - TypeList, AnnotationSetRefList, AnnotationSetItem, CodeItem, @@ -1877,7 +2064,6 @@ impl FragSection { Self::ClassDefItem, Self::CallSiteIdItem, Self::MethodHandleItem, - Self::TypeList, Self::AnnotationSetRefList, Self::AnnotationSetItem, Self::CodeItem, @@ -1886,8 +2072,7 @@ impl FragSection { Self::AnnotationItem, Self::EncodedArrayItem, Self::AnnotationsDirectoryItem, - // must be last because contains offsets in Uleb, - // so size change when linking ! + // /!\ contains offsets in Uleb, so size change when linking ! Self::ClassDataItem, // Self::HiddenapiClassDataItem, ]; @@ -1897,17 +2082,16 @@ impl FragSection { Self::ClassDefItem => 0, Self::CallSiteIdItem => 1, Self::MethodHandleItem => 2, - Self::TypeList => 3, - Self::AnnotationSetRefList => 4, - Self::AnnotationSetItem => 5, - Self::CodeItem => 6, - Self::StringDataItem => 7, - Self::DebugInfoItem => 8, - Self::AnnotationItem => 9, - Self::EncodedArrayItem => 10, - Self::AnnotationsDirectoryItem => 11, - Self::ClassDataItem => 12, - // Self::HiddenapiClassDataItem => 13, + Self::AnnotationSetRefList => 3, + Self::AnnotationSetItem => 4, + Self::CodeItem => 5, + Self::StringDataItem => 6, + Self::DebugInfoItem => 7, + Self::AnnotationItem => 8, + Self::EncodedArrayItem => 9, + Self::AnnotationsDirectoryItem => 10, + Self::ClassDataItem => 11, + // Self::HiddenapiClassDataItem => 12, } } @@ -1916,7 +2100,6 @@ impl FragSection { Self::ClassDefItem => Some(0x20), Self::CallSiteIdItem => Some(4), Self::MethodHandleItem => Some(8), - Self::TypeList => None, Self::AnnotationSetRefList => None, Self::AnnotationSetItem => None, Self::CodeItem => None, @@ -1953,7 +2136,6 @@ impl FragSection { Self::ClassDefItem => 4, Self::CallSiteIdItem => 1, Self::MethodHandleItem => 4, - Self::TypeList => 4, Self::AnnotationSetRefList => 4, Self::AnnotationSetItem => 4, Self::CodeItem => 4, @@ -2124,6 +2306,7 @@ impl FragSectionManager { } /// Index that associate a type to its local id in a fragment. +#[derive(Debug, Clone)] struct FragIndex { pub strings: HashMap, pub types: HashMap, @@ -2131,3 +2314,38 @@ struct FragIndex { pub fields: HashMap, pub methods: HashMap, } + +/// The link state of a fragment. Describe the linking of a fragment and the related data. +#[derive(Debug, Clone)] +enum FragLinkState { + /// The fragment is not linked + Unlinked, + /// The index used in the fragment are linked of beeing linked. + /// The new local addresses of the element whose size depend on the value + /// of the index are stored here. + LinkedIdx { + code_item_relocation: HashMap, + debug_info_item_relocation: HashMap, + annotation_item_relocation: HashMap, + encoded_array_relocation: HashMap, // TODO only one? + }, +} + +impl FragLinkState { + fn start_linking_idx(&mut self) -> Result<()> { + match self { + Self::Unlinked => { + *self = Self::LinkedIdx { + code_item_relocation: HashMap::::new(), + debug_info_item_relocation: HashMap::::new(), + annotation_item_relocation: HashMap::::new(), + encoded_array_relocation: HashMap::::new(), + }; + Ok(()) + } + _ => { + bail!("Cannot link the idx in a fragment that is already linked"); + } + } + } +} diff --git a/androscalpel/src/dex_writer.rs b/androscalpel/src/dex_writer.rs index f967970..840872c 100644 --- a/androscalpel/src/dex_writer.rs +++ b/androscalpel/src/dex_writer.rs @@ -1,3255 +1,130 @@ //! The structure that generate a .dex from classes. +use std::collections::{HashSet, VecDeque}; -use std::collections::{HashMap, HashSet, VecDeque}; -use std::io; -use std::io::{Cursor, Seek, SeekFrom, Write}; - -use adler::Adler32; -use anyhow::{anyhow, bail, Context}; -use log::debug; -use sha1::{Digest, Sha1}; - -use crate::Result; -use crate::*; -use androscalpel_serializer::*; - -use crate::ins::{CallSite, Instruction}; -use crate::instructions::*; -use androscalpel_serializer::Instruction as InsFormat; +use crate::dex_fragment::DexFragment; +use crate::{DexString, IdField, IdMethod, IdMethodType, IdType}; #[derive(Debug, Clone)] pub struct DexWriter { - header: HeaderItem, - /// The strings in the dex file. Initially values are set to zero. - /// Once the strings are sorted, the values of the map are set to the - /// strings idx - strings: HashMap, - /// The types in the dex file. Initially values are set to zero. - /// Once the types are sorted, the values of the map are set to the - /// types idx - type_ids: HashMap, - /// The prototypes in the dex file. Initially values are set to zero. - /// Once the prototypes are sorted, the values of the map are set to the - /// prototypes idx - proto_ids: HashMap, - /// The field ids in the dex file. Initially values are set to zero. - /// Once the fields are sorted, the values of the map are set to the - /// fields idx - field_ids: HashMap, - /// The methods ids in the dex file. Initially values are set to zero. - /// Once the methods are sorted, the values of the map are set to the - /// method idx - method_ids: HashMap, - /// The classes defined in the dex file. Initially values are set to the classes and zero. - /// Once the class definitions are sorted, the values are set to the classes and there - /// index in the `class_defs` section. - class_defs: HashMap, - /// The call sites refered to in the bytecode. - call_site_ids: Vec, - /// A struct that keep track of sections size, nb elements and offsets. - section_manager: SectionManager, - /// The string_data section. Once populated, the elements must be sorted according to the spec. - string_data_list: Vec, - /// The type_ids sections. Once populated, the elements must be sorted according to the spec. - type_ids_list: Vec, - /// The proto_ids section. Once populated, the elements must be sorted according to the spec. - /// the `parameter_off` field of the prototypes must be linked before serializing the section. - proto_ids_list: Vec, - /// The field_ids section. Once populated, the elements must be sorted according to the spec. - field_ids_list: Vec, - /// The method_ids section. Once populated, the elements must be sorted according to the spec. - method_ids_list: Vec, - /// The class_defs section. Once populated, the elements must be sorted according to the spec. - /// The `interfaces_off`, `annotations_off`, `class_data_off` and `static_values_off` fields - /// must be linked before serializing. Before liking, the `annotations_off`, `class_data_off` - /// and `static_values_off` field take the value of the offset from the start of theire - /// respective section (or zero if the class does not have a value associated to the field). - class_defs_list: Vec, - /// The class_data section. - class_data_list: Vec, - /// The type lists found in the classes associated to their index in the type_lists section. - type_lists_index: HashMap, - /// The type_lists section and the offset of the lists inside the section. - type_lists_with_offset: Vec<(TypeList, u32)>, - /// The encoded_array_items section. - encoded_array_items: Vec, - /// The annotations_directory_item section. - annotations_directory_items: Vec, - /// The annotation_set_item section. - annotation_set_items: Vec, - /// The annotation item section. - annotation_items: Vec, - /// The annotation_set_ref_list section. - annotation_set_lists: Vec, - /// The method_handles section. - method_handles: Vec, - /// The code_items sections. - code_items: Vec, - /// The debug info items section. - debug_info_items: Vec, - /// The map_list - map_list: MapList, - /// Map the **local** offset of **unlinked** class data offset to the **local** - /// offset of the **linked**. - /// - /// local offset means the offset of the first data item in the section is 0 (i.e. not - /// the offset from the beginning of the file). - /// - /// This hack is necessary (well, maybe not necessary but that would require some refactoring) - /// because someone thought that encoding offsets in Uleb format was a good idea. It was not. - /// The size of the encoded offset depend on the value of the offset, so the linking change the - /// size of the struct, and doing so change the offset of the next struct, ect. - /// - /// When generating the structs refering to `class_data_item`s, the local offset of the - /// unlinked `class_data_item` is known, but not the linked offset. This struct is filled - /// when linking the `class_data_item`s, and can then be used to link the structs refering to - /// the `class_data_item`s. - corrected_class_data_offset: HashMap, + fragments: VecDeque, } impl Default for DexWriter { fn default() -> Self { Self { - header: HeaderItem { - magic: DexFileMagic { - //version: [0x30, 0x33, 0x39], - version: [0x30, 0x33, 0x37], - }, // TODO: find a better default version - checksum: 0, - signature: [0u8; 20], - file_size: 0, - header_size: 0x70, - endian_tag: EndianConstant::EndianConstant, - link_size: 0, - link_off: 0, - map_off: 0, - string_ids_size: 0, - string_ids_off: 0, - type_ids_size: 0, // At most 0xffff - type_ids_off: 0, - proto_ids_size: 0, // At most 0xffff - proto_ids_off: 0, - field_ids_size: 0, - field_ids_off: 0, - method_ids_size: 0, - method_ids_off: 0, - class_defs_size: 0, - class_defs_off: 0, - data_size: 0, // Must be an even multiple of sizeof(uint) -> % 8 = 0 - data_off: 0, - }, - strings: HashMap::new(), - type_ids: HashMap::new(), - proto_ids: HashMap::new(), - field_ids: HashMap::new(), - method_ids: HashMap::new(), - class_defs: HashMap::new(), - call_site_ids: vec![], - section_manager: SectionManager::default(), - string_data_list: vec![], - type_ids_list: vec![], - proto_ids_list: vec![], - field_ids_list: vec![], - method_ids_list: vec![], - class_defs_list: vec![], - class_data_list: vec![], - type_lists_index: HashMap::new(), - type_lists_with_offset: vec![], - map_list: MapList::default(), - encoded_array_items: vec![], - method_handles: vec![], - code_items: vec![], - annotations_directory_items: vec![], - annotation_set_items: vec![], - annotation_items: vec![], - annotation_set_lists: vec![], - debug_info_items: vec![], - corrected_class_data_offset: HashMap::new(), + fragments: VecDeque::new(), } } } -pub enum DexWritterError { - OutOfSpace(String), -} - impl DexWriter { pub fn new() -> Self { Self::default() } - pub fn add_class(&mut self, class: &Class) -> Result<(), DexWritterError> { - debug!("Adding class {} to dex builder", class.descriptor.__str__()); - let new_strings = class.get_all_strings(); - - let new_types = class.get_all_types(); - let new_nb_types = self.type_ids.len() - + new_types - .iter() - .filter(|ty| self.type_ids.get(ty).is_none()) - .count(); - if new_nb_types >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many types for one dex file".into(), - )); - } - - let new_protos = class.get_all_protos(); - let new_nb_protos = self.proto_ids.len() - + new_protos - .iter() - .filter(|proto| self.proto_ids.get(proto).is_none()) - .count(); - if new_nb_protos >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many prototypes for one dex file".into(), - )); - } - - let new_field_ids = class.get_all_field_ids(); - let new_nb_field_ids = self.field_ids.len() - + new_field_ids - .iter() - .filter(|field| self.field_ids.get(field).is_none()) - .count(); - if new_nb_field_ids >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many field ids for one dex file".into(), - )); - } - - let new_method_ids = class.get_all_method_ids(); - let new_nb_method_ids = self.method_ids.len() - + new_method_ids - .iter() - .filter(|meth| self.method_ids.get(meth).is_none()) - .count(); - if new_nb_method_ids >= u16::MAX as usize { - return Err(DexWritterError::OutOfSpace( - "To many method ids for one dex file".into(), - )); - } - - for string in new_strings { - self.strings.insert(string, 0); - } - for ty in new_types { - self.type_ids.insert(ty, 0); - } - for proto in new_protos { - self.proto_ids.insert(proto, 0); - } - for field in new_field_ids { - self.field_ids.insert(field, 0); - } - for method in new_method_ids { - self.method_ids.insert(method, 0); - } - self.class_defs - .insert(class.descriptor.clone(), (class.clone(), 0)); - Ok(()) + pub fn empty(&self) -> bool { + self.fragments.is_empty() } - pub fn add_string(&mut self, string: DexString) { - self.strings.insert(string, 0); - } - - fn gen_string_data_section(&mut self) -> Result<()> { - debug!("Sort string and generate string_data_item and string_ids sections"); - let mut string_ids_list: Vec = self.strings.keys().cloned().collect(); - string_ids_list.sort(); - for (idx, string) in string_ids_list.iter().enumerate() { - self.strings - .entry(string.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::StringIdItem, None); - self.section_manager - .add_elt(Section::StringDataItem, Some(string.0.size())); - } - self.string_data_list = string_ids_list - .into_iter() - .map(|string| string.into()) - .collect(); - Ok(()) - } - - fn gen_type_ids_section(&mut self) -> Result<()> { - debug!("Sort types and generate type_id_item section"); - let mut type_ids_list: Vec = self.type_ids.keys().cloned().collect(); - type_ids_list.sort(); - for (idx, ty) in type_ids_list.iter().enumerate() { - self.type_ids.entry(ty.clone()).and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::TypeIdItem, None); - } - let mut type_ids_list_aux = vec![]; - for ty in type_ids_list.into_iter() { - type_ids_list_aux.push(TypeIdItem { - descriptor_idx: *self.strings.get(&ty.0).ok_or(anyhow!( - "String {} (name of type {}) not found in dex builder", - ty.0.__repr__(), - ty.__repr__() - ))? as u32, - }); - } - self.type_ids_list = type_ids_list_aux; - Ok(()) - } - - fn gen_proto_ids_section(&mut self) -> Result<()> { - debug!("Sort prototypes and generate proto_id_item section"); - let mut proto_ids_list: Vec = self.proto_ids.keys().cloned().collect(); - proto_ids_list.sort(); - for (idx, proto) in proto_ids_list.iter().enumerate() { - self.proto_ids - .entry(proto.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::ProtoIdItem, None); - } - let mut proto_ids_list_aux = vec![]; - for proto in proto_ids_list { - proto_ids_list_aux.push(ProtoIdItem { - shorty_idx: *self.strings.get(&proto.shorty).ok_or(anyhow!( - "String {}, (shorty of prototype {}) not found in dex builder", - proto.shorty.__repr__(), - proto.__repr__() - ))? as u32, - return_type_idx: *self.type_ids.get(&proto.return_type).ok_or(anyhow!( - "Type {}, (return type of prototype {}) not found in dex builder", - proto.shorty.__repr__(), - proto.__repr__() - ))? as u32, - parameters_off: 0, // Will be linked later - }); - } - self.proto_ids_list = proto_ids_list_aux; - Ok(()) - } - - fn gen_field_ids_section(&mut self) -> Result<()> { - debug!("Sort field ids and generate field_ids_item"); - let mut field_ids_list: Vec = self.field_ids.keys().cloned().collect(); - field_ids_list.sort(); - for (idx, field_id) in field_ids_list.iter().enumerate() { - self.field_ids - .entry(field_id.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::FieldIdItem, None); - } - let mut field_ids_list_aux = vec![]; - for field in field_ids_list.into_iter() { - field_ids_list_aux.push(FieldIdItem { - class_idx: *self.type_ids.get(&field.class_).ok_or(anyhow!( - "Type {} (class of field {}) not found in dex builder", - field.class_.__repr__(), - field.__repr__() - ))? as u16, - type_idx: *self.type_ids.get(&field.type_).ok_or(anyhow!( - "Type {} (type of field {}) not found in dex builder", - field.type_.__repr__(), - field.__repr__() - ))? as u16, - name_idx: *self.strings.get(&field.name).ok_or(anyhow!( - "String {} (name of field {}) not found in dex builder", - field.name.__repr__(), - field.__repr__() - ))? as u32, - }); - } - self.field_ids_list = field_ids_list_aux; - - Ok(()) - } - - fn gen_method_ids_section(&mut self) -> Result<()> { - debug!("Sort method ids and generate method_id_item section"); - let mut method_ids_list: Vec = self.method_ids.keys().cloned().collect(); - method_ids_list.sort(); - for (idx, method_id) in method_ids_list.iter().enumerate() { - self.method_ids - .entry(method_id.clone()) - .and_modify(|val| *val = idx); - self.section_manager.add_elt(Section::MethodIdItem, None); - } - let mut method_ids_list_aux = vec![]; - for method in method_ids_list.into_iter() { - method_ids_list_aux.push(MethodIdItem { - class_idx: *self.type_ids.get(&method.class_).ok_or(anyhow!( - "Type {} (class of method {}) not found in dex builder", - method.class_.__repr__(), - method.__repr__() - ))? as u16, - proto_idx: *self.proto_ids.get(&method.proto).ok_or(anyhow!( - "Prototype {} (signature of method {}) not found in dex builder", - method.proto.__repr__(), - method.__repr__() - ))? as u16, - name_idx: *self.strings.get(&method.name).ok_or(anyhow!( - "String {} (name of method {}) not found in dex builder", - method.name.__repr__(), - method.__repr__() - ))? as u32, - }); - } - self.method_ids_list = method_ids_list_aux; - Ok(()) - } - - /// Insert a code_item. - /// - /// # Warning - /// - /// This is currently a stub that probably serialize invalid references to data. - fn insert_code_item(&mut self, method_id: IdMethod, direct_methods: bool) -> Result<()> { - let code = if direct_methods { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .direct_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - } else { - self.class_defs - .get(&method_id.class_) - .unwrap() - .0 - .virtual_methods - .get(&method_id) - .unwrap() - .code - .as_ref() - .unwrap() - .clone() - }; - // Estimate instructions addresses - let mut min_addr = 0; - let mut max_addr = 0; - let mut label_min_max_addrs: HashMap = HashMap::new(); - - for ins in &code.insns { - match ins { - Instruction::Label(Label { name }) => { - label_min_max_addrs.insert(name.clone(), (min_addr, max_addr)); - min_addr += ins.min_ins_size() / 2; - max_addr += ins.max_ins_size() / 2; - } - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let size = ins.get_raw_ins(*string_idx).size() / 2; - min_addr += size; - max_addr += size; - } - _ => { - min_addr += ins.min_ins_size() / 2; - max_addr += ins.max_ins_size() / 2; - } - } - } - // Compute instruction size and precise addresses - let mut addr = 0; - let mut label_addrs = HashMap::new(); - let mut goto_sizes = vec![]; - for ins in &code.insns { - match ins { - Instruction::Label(Label { name }) => { - label_addrs.insert(name.clone(), addr); - addr += ins.max_ins_size() / 2; - } - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - addr += ins.get_raw_ins(*string_idx).size() / 2; - } - Instruction::Goto(Goto { label }) => { - let (min_addr, max_addr) = label_min_max_addrs - .get(label) - .ok_or(anyhow!("Label {label} not found in label estimation map"))?; - let size = Goto::size_from_branch_offset_interval(addr, *min_addr, *max_addr)?; - goto_sizes.push(size); - addr += size / 2; - } - _ => addr += ins.ins_size()? / 2, - } - } - // Serialize instructions - let mut tries = vec![]; - let mut handlers = EncodedCatchHandlerList { list: vec![] }; - let mut handler_off = 0; - let mut insns = vec![]; - let mut payloads = vec![]; - let mut goto_idx = 0; - let mut payload_addr = addr; - if payload_addr % 2 != 0 { - payload_addr += 1; // For switch and array table alignment - } - addr = 0; - for ins in &code.insns { - match ins { - Instruction::ConstString(ins) => { - let string_idx = self.strings.get(&ins.lit).ok_or(anyhow!( - "String {} (found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*string_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstClass(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::CheckCast(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InstanceOf(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::NewInstance(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Class {} (type of class found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::NewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.lit).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", - ins.lit.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::FilledNewArray(ins) => { - let class_idx = *self.type_ids.get(&ins.type_).ok_or(anyhow!( - "Type {} (type found in code of {}) not found in dex builder", - ins.type_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(class_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::FillArrayData(ins) => { - let payload = InsFormat::FormatFillArrayDataPayload { - elt_width: ins.elt_width, - data: ins.data.clone(), - }; - if payload_addr % 2 != 0 { - // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1347 - // The ART check if the array data table is 4 bytes aligned (= 2 ins alligned) - // TODO: check how it is donne in android and other dex generation code. - let nop = Instruction::Nop(Nop).get_raw_ins()?; - payload_addr += nop.size() / 2; - payloads.push(nop); - } - let data_offset = payload_addr as i32 - addr as i32; - payload_addr += payload.size() / 2; - payloads.push(payload); - let ins = ins.get_raw_ins(data_offset); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Goto(ins) => { - let goto_size = goto_sizes[goto_idx]; - goto_idx += 1; - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - let ins = ins.get_raw_ins(branch_offset, goto_size); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Switch(ins) => { - let mut key_targets = vec![]; - for (key, label) in &ins.branches { - let label_addr = label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found goto with this label", - label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - key_targets.push((*key, branch_offset)); - } - key_targets.sort_by_key(|(key, _)| *key); - let payload = if ins.is_packed() { - let (first_key, _) = *key_targets.first().ok_or(anyhow!( - "Found empty swith in code of {}", - method_id.__repr__() - ))?; - let targets: Vec<_> = - key_targets.into_iter().map(|(_, target)| target).collect(); - InsFormat::FormatPackedSwitchPayload { first_key, targets } - } else { - InsFormat::FormatSparseSwitchPayload { key_targets } - }; - if payload_addr % 2 != 0 { - // https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/verifier/method_verifier.cc;drc=e8c3e7be783937a340cd4f3280b69962d6f1ea0c;l=1464 - // The ART check if the switch table is 4 bytes aligned (= 2 ins alligned) - // TODO: check how it is donne in android and other dex generation code. - let nop = Instruction::Nop(Nop).get_raw_ins()?; - payload_addr += nop.size() / 2; - payloads.push(nop); - } - let data_offset = payload_addr as i32 - addr as i32; - payload_addr += payload.size() / 2; - payloads.push(payload); - let ins = ins.get_raw_ins(data_offset); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfEq(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfNe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLt(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGt(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLe(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfEqZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfNeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLtZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfGtZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IfLeZ(ins) => { - let label_addr = label_addrs.get(&ins.label).ok_or(anyhow!( - "Label {} not found in code of {}, but found if with this label", - ins.label, - method_id.__repr__() - ))?; - let branch_offset = *label_addr as i32 - addr as i32; - if branch_offset > i16::MAX as i32 || branch_offset < i16::MIN as i32 { - bail!( - "Found an if that jump to far from the instruction in code of {}", - method_id.__repr__() - ); - } - let ins = ins.get_raw_ins(branch_offset as i16); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGet(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IGetShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPut(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::IPutShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGet(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SGetShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPut(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutWide(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutObject(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutBoolean(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutByte(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutChar(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::SPutShort(ins) => { - let field = &ins.field; - let field_idx = self.field_ids.get(field).ok_or(anyhow!( - "Field {} (field of class {}, found in code of {}) not found in dex builder", - field.__repr__(), - field.class_.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*field_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeVirtual(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeSuper(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeDirect(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeStatic(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeInterface(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let ins = ins.get_raw_ins(meth_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokePolymorphic(ins) => { - let meth = &ins.method; - let meth_idx = self.method_ids.get(meth).ok_or(anyhow!( - "Method {} (method of class {}, found in code of {}) not found in dex builder", - meth.__repr__(), - meth.class_.__repr__(), - method_id.__repr__() - ))?; - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() - ))?; - debug_assert!( - *meth_idx <= u16::MAX as usize, - "methode id too big for invoke instruction" - ); - debug_assert!( - *proto_idx <= u16::MAX as usize, - "proto id too big for invoke instruction" - ); - let meth_idx = *meth_idx as u16; - let proto_idx = *proto_idx as u16; - let ins = ins.get_raw_ins(meth_idx, proto_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::InvokeCustom(ins) => { - let call_site_idx = self.call_site_ids.len(); - self.insert_call_site_item(&ins.call_site)?; - let ins = ins.get_raw_ins(call_site_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstMethodHandle(ins) => { - let method_handle_idx = self.method_handles.len(); - self.insert_method_handle(&ins.handle)?; - let ins = ins.get_raw_ins(method_handle_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::ConstMethodType(ins) => { - let proto_idx = self.proto_ids.get(&ins.proto).ok_or(anyhow!( - "Prototype {} (found in code of {}) not found in dex builder", - ins.proto.__repr__(), - method_id.__repr__() - ))?; - let ins = ins.get_raw_ins(*proto_idx); - addr += ins.size() / 2; - insns.push(ins); - } - Instruction::Try(try_) => { - let end_block_addr = *label_addrs.get(&try_.end_label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try with this label", - &try_.end_label, - method_id.__repr__() - ))?; - if end_block_addr < addr { - bail!( - "Found end label of a try block before the try instruction in code of {}", - method_id.__repr__() - ) - } - let try_item = TryItem { - start_addr: addr as u32, - insn_count: (end_block_addr - addr) as u16, - handler_off: handler_off as u16, // will be ajusted once the size of the - // handler list object is known - }; - tries.push(try_item); - let mut catches = EncodedCatchHandler { - handlers: vec![], - catch_all_addr: None, - }; - for (ty, label) in &try_.handlers { - let type_idx = Uleb128(*self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} captured by a try block in {}\ - in the dex builder", - ty.__repr__(), - method_id.__repr__() - ))? as u32); - let addr = Uleb128(*label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ - with this label as catch for type {}", - &try_.end_label, - method_id.__repr__(), - ty.__repr__(), - ))? as u32); - catches - .handlers - .push(EncodedTypeAddrPair { type_idx, addr }); - } - if let Some(ref label) = try_.default_handler { - let catch_all_addr = *label_addrs.get(label).ok_or(anyhow!( - "Label {} not found in code of {}, but found try \ - with this label as catch all", - &try_.end_label, - method_id.__repr__() - ))?; - catches.catch_all_addr = Some(Uleb128(catch_all_addr as u32)); - } - handler_off += catches.size(); - handlers.list.push(catches); - } - Instruction::Label(_) => (), - _ => { - let ins = ins.get_raw_ins().with_context(|| { - format!( - "Failed to convert instruction {} (found in code of {}) to raw instruction", - ins.__repr__(), - method_id.__repr__() - ) - })?; - addr += ins.size() / 2; - insns.push(ins); - } - } - } - if addr % 2 != 0 { - // make sure the payload section is 4 bytes aligned - let nop = Instruction::Nop(Nop).get_raw_ins()?; - //addr += nop.size() / 2; - insns.push(nop); - } - insns.extend(payloads); - - for try_ in &mut tries { - try_.handler_off += handlers.size_field().size() as u16; - } - - let debug_info_off = if code.debug_info.1.is_empty() && code.parameter_names.is_none() { - 0 - } else { - let debug_info_off = self - .section_manager - .get_aligned_size(Section::DebugInfoItem); - let mut cursor = Cursor::new(code.debug_info.1); - let mut item = DebugInfoItem { - line_start: Uleb128(code.debug_info.0), - parameter_names: vec![], - bytecode: Vec::::deserialize(&mut cursor, DbgBytecode::EndSequence)?, - }; - if let Some(parameter_names) = code.parameter_names { - for name in ¶meter_names { - if let Some(name) = name { - item.parameter_names - .push(Uleb128p1(*self.strings.get(name).ok_or(anyhow!( - "String {} (name of param of {}) not found", - name.__str__(), - method_id.__repr__() - ))? as u32)); - } else { - item.parameter_names.push(NO_INDEX); - } - } - } - self.section_manager - .add_elt(Section::DebugInfoItem, Some(item.size())); - self.debug_info_items.push(item); - debug_info_off + 1 - }; - let handlers = if handlers.list.is_empty() { - None - } else { - Some(handlers) - }; - let item = CodeItem { - registers_size: code.registers_size, - ins_size: code.ins_size, - outs_size: code.outs_size, - debug_info_off, // linked in link_debug_info() - insns, - tries, - handlers, - }; - self.section_manager - .add_elt(Section::CodeItem, Some(item.size())); - self.code_items.push(item); - Ok(()) - } - - /// Insert annotation associated to a class. - /// - - /// Insert a class_data_item in the class_data section (in data). - /// - /// # Note - /// - /// code_item objects are 4 bytes aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_class_data_item(&mut self, class_id: &IdType) -> Result<()> { - let mut data = ClassDataItem::default(); - let (class, _) = self.class_defs.get(class_id).unwrap(); - - let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); - static_fields.sort(); - let mut last_field_id = 0; - for id in &static_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( - "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let field_idx_diff = Uleb128((idx - last_field_id) as u32); - last_field_id = *idx; - let access_flags = Uleb128(class.static_fields.get(id).unwrap().get_raw_access_flags()); - data.static_fields.push(EncodedField { - field_idx_diff, - access_flags, - }); - } - - let mut instance_fields: Vec = class.instance_fields.keys().cloned().collect(); - instance_fields.sort(); - let mut last_field_id = 0; - for id in &instance_fields { - let idx = self.field_ids.get(id).ok_or(anyhow!( - "Field {} (field of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let field_idx_diff = Uleb128((idx - last_field_id) as u32); - last_field_id = *idx; - let access_flags = Uleb128( - class - .instance_fields - .get(id) - .unwrap() - .get_raw_access_flags(), - ); - data.instance_fields.push(EncodedField { - field_idx_diff, - access_flags, - }); - } - - let mut direct_methods: Vec = class.direct_methods.keys().cloned().collect(); - direct_methods.sort(); - let mut last_method_id = 0; - for id in &direct_methods { - // &mut vs & of self and class make things difficult... - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( - "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let method_idx_diff = Uleb128((idx - last_method_id) as u32); - last_method_id = *idx; - let access_flags = - Uleb128(class.direct_methods.get(id).unwrap().get_raw_access_flags()); - // No if let because ownership gunfooterie - let code_off = if class.direct_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), true)?; - Uleb128(code_off + 1) + /// Take as many fragments as possible and convert them to a dex file. + pub fn generate_next_dex_file(&mut self) -> Vec { + let mut fragments = vec![]; + let mut string_set: HashSet = HashSet::new(); + let mut type_set: HashSet = HashSet::new(); + let mut proto_set: HashSet = HashSet::new(); + let mut field_set: HashSet = HashSet::new(); + let mut method_set: HashSet = HashSet::new(); + let mut type_list_set: HashSet> = HashSet::new(); + let mut nb_method_handle = 0; + let mut nb_method_handle_before = vec![]; + loop { + let new_fragment = if let Some(new_fragment) = self.fragments.pop_front() { + new_fragment } else { - Uleb128(0) + break; }; - data.direct_methods.push(EncodedMethod { - method_idx_diff, - access_flags, - code_off, // Will be relinked once the offset of the code item section is known - }); - } - - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut virtual_methods: Vec = class.virtual_methods.keys().cloned().collect(); - virtual_methods.sort(); - let mut last_method_id = 0; - for id in &virtual_methods { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let idx = self.method_ids.get(id).ok_or(anyhow!( - "Method {} (method of class {}) not found in dex builder", - id.__repr__(), - class.__repr__() - ))?; - let method_idx_diff = Uleb128((idx - last_method_id) as u32); - last_method_id = *idx; - let access_flags = Uleb128( - class - .virtual_methods - .get(id) - .unwrap() - .get_raw_access_flags(), - ); - // No if let because ownership gunfooterie - let code_off = if class.virtual_methods.get(id).unwrap().code.is_some() { - let code_off = self.section_manager.get_aligned_size(Section::CodeItem); - self.insert_code_item(id.clone(), false)?; - Uleb128(code_off + 1) - } else { - Uleb128(0) - }; - data.virtual_methods.push(EncodedMethod { - method_idx_diff, - access_flags, - code_off, // Will be relinked once the offset of the code item section is known - }); - } - self.section_manager - .add_elt(Section::ClassDataItem, Some(data.size())); - //assert_eq!(data.size(), data.serialize_to_vec().unwrap().len()); - self.class_data_list.push(data); - Ok(()) - } - - /// Insert a [`MethodHandle`]. - pub fn insert_method_handle(&mut self, handle: &MethodHandle) -> Result<()> { - let (field_or_method_id, method_handle_type) = match handle { - MethodHandle::StaticPut(StaticPut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::StaticPut, - ), - MethodHandle::StaticGet(StaticGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::StaticGet, - ), - MethodHandle::InstancePut(InstancePut(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::InstancePut, - ), - MethodHandle::InstanceGet(InstanceGet(field)) => ( - *self.field_ids.get(field).ok_or(anyhow!( - "Field {} not found in dex writer", - field.__repr__() - ))? as u16, - MethodHandleType::InstanceGet, - ), - MethodHandle::InvokeStatic(InvokeStatic(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeStatic, - ), - MethodHandle::InvokeInstance(InvokeInstance(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeInstance, - ), - MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeConstructor, - ), - MethodHandle::InvokeDirect(InvokeDirect(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeDirect, - ), - MethodHandle::InvokeInterface(InvokeInterface(meth)) => ( - *self.method_ids.get(meth).ok_or(anyhow!( - "Method {} not found in dex writer", - meth.__repr__() - ))? as u16, - MethodHandleType::InvokeInterface, - ), - }; - self.method_handles.push(MethodHandleItem { - method_handle_type, - field_or_method_id, - unused1: 0, - unused2: 0, - }); - Ok(()) - } - - /// Convert a [`DexValue`] to an [`EncodedValue`]. - /// - /// # Warning - /// - /// This method can insert element in the dex file like method_handles. - pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result { - match value { - DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), - DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), - DexValue::Char(DexChar(val)) => Ok(EncodedValue::Char(*val)), - DexValue::Int(DexInt(val)) => Ok(EncodedValue::Int(*val)), - DexValue::Long(DexLong(val)) => Ok(EncodedValue::Long(*val)), - DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), - DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), - DexValue::MethodType(val) => Ok(EncodedValue::MethodType( - *self.proto_ids.get(val).ok_or(anyhow!( - "Prototype {} not found in dex writer", - val.__repr__() - ))? as u32, - )), - DexValue::MethodHandle(val) => { - // TODO: move to a method - let idx = self.method_handles.len() as u32; - self.insert_method_handle(val)?; - Ok(EncodedValue::MethodHandle(idx)) + if type_set.len() + new_fragment.type_ids().len() > u16::MAX as usize + && type_set.len() + + new_fragment + .type_ids() + .iter() + .filter(|ty| type_set.contains(*ty)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - DexValue::String(val) => Ok(EncodedValue::String( - *self - .strings - .get(val) - .ok_or(anyhow!("String {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Type(val) => Ok(EncodedValue::Type( - *self - .type_ids - .get(val) - .ok_or(anyhow!("Type {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Field(val) => Ok(EncodedValue::Field( - *self - .field_ids - .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Method(val) => Ok(EncodedValue::Method( - *self - .method_ids - .get(val) - .ok_or(anyhow!("Method {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( - *self - .field_ids - .get(val) - .ok_or(anyhow!("Field {} not found in dex writer", val.__repr__()))? - as u32, - )), - DexValue::Array(DexArray(arr)) => { - let mut values = vec![]; - for val in arr { - values.push( - self.dex_value_to_encoded_value(val) - .context("Error while serializing a array")?, - ); - } - Ok(EncodedValue::Array(EncodedArray { values })) + if proto_set.len() + new_fragment.proto_ids().len() > u16::MAX as usize + && proto_set.len() + + new_fragment + .proto_ids() + .iter() + .filter(|proto| proto_set.contains(*proto)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - DexValue::Annotation(val) => Ok(EncodedValue::Annotation( - self.dex_annotation_to_encoded_annotation(val.clone())?, - )), - DexValue::Null(DexNull) => Ok(EncodedValue::Null), - DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), - } - } - - /// Insert an encoded_array in the encoded_array_item section. - fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { - let mut values = vec![]; - for value in array { - values.push(self.dex_value_to_encoded_value(&value)?); - } - let item = EncodedArrayItem { - value: EncodedArray { values }, - }; - self.section_manager - .add_elt(Section::EncodedArrayItem, Some(item.size())); - self.encoded_array_items.push(item); - Ok(()) - } - - /// Insert a [`CallSite`] to the encoded array items - /// - /// # Warning - /// - /// This method can insert element in the dex file like method_handles. - pub fn insert_call_site_item(&mut self, call_site: &CallSite) -> Result<()> { - let mut values = vec![]; - values.push(DexValue::MethodHandle(call_site.method_handle.clone())); - values.push(DexValue::String(call_site.name.clone())); - values.push(DexValue::MethodType(call_site.type_.clone())); - values.extend(call_site.args.iter().cloned()); - self.call_site_ids.push(CallSiteIdItem { - call_site_off: self - .section_manager - .get_aligned_size(Section::EncodedArrayItem), - }); // linked in link_call_site_ids() - self.section_manager.add_elt(Section::CallSiteIdItem, None); - self.insert_encoded_array_item(DexArray(values)) - } - - /// Insert the encoded_array_item encoding the static_values of a class. - fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut static_fields: Vec = class.static_fields.keys().cloned().collect(); - static_fields.sort(); - let mut array = vec![]; - let mut last_defined_field_index = 0; - for (idx, f) in static_fields.iter().enumerate() { - if class.static_fields.get(f).unwrap().value.is_some() { - last_defined_field_index = idx; + if field_set.len() + new_fragment.field_ids().len() > u16::MAX as usize + && field_set.len() + + new_fragment + .field_ids() + .iter() + .filter(|field| field_set.contains(*field)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } - } - for f in &static_fields[..=last_defined_field_index] { - let field = class.static_fields.get(f).unwrap(); - if let Some(val) = field.value.as_ref() { - array.push(val.clone()); - } else { - array.push(field.descriptor.type_.get_default_value().ok_or(anyhow!( - "The type {} (for field {} in class {}) does not have a default value", - field.descriptor.type_.__repr__(), - field.descriptor.__repr__(), - class_id.__repr__() - ))?); + if method_set.len() + new_fragment.method_ids().len() > u16::MAX as usize + && method_set.len() + + new_fragment + .method_ids() + .iter() + .filter(|meth| method_set.contains(*meth)) + .count() + > u16::MAX as usize + { + self.fragments.push_front(new_fragment); + break; } + string_set.extend(new_fragment.strings().iter().cloned()); + type_set.extend(new_fragment.type_ids().iter().cloned()); + proto_set.extend(new_fragment.proto_ids().iter().cloned()); + field_set.extend(new_fragment.field_ids().iter().cloned()); + method_set.extend(new_fragment.method_ids().iter().cloned()); + type_list_set.insert(new_fragment.interfaces().to_vec()); + nb_method_handle_before.push(nb_method_handle); + nb_method_handle += new_fragment.method_handles().len(); + fragments.push(new_fragment); } - self.insert_encoded_array_item(DexArray(array)) - .with_context(|| { - format!( - "Failed to serialize static values of class {}", - class_id.__repr__() - ) - }) - } - - fn dex_annotation_to_encoded_annotation( - &mut self, - DexAnnotation { type_, elements }: DexAnnotation, - ) -> Result { - let mut encoded_elements = vec![]; - - let mut elements_names: Vec<_> = elements.keys().collect(); - elements_names.sort(); - - for name in elements_names { - let elt = elements.get(name).unwrap(); - encoded_elements.push(AnnotationElement { - name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( - "{} (annotation element name) not found in dex builder", - name.__str__() - ))? as u32), - value: self.dex_value_to_encoded_value(elt)?, - }); - } - Ok(EncodedAnnotation { - type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( - "Annotation type {} not found in dex builder", - type_.__repr__(), - ))? as u32), - elements: encoded_elements, - }) - } - - /// Insert the annnotations set for a class. - fn insert_class_annotation_set(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let mut annotations = class.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annnotations set for a class. - fn insert_field_annotation_set( - &mut self, - field_id: &IdField, - is_static_field: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&field_id.class_).unwrap(); - let field = if is_static_field { - class.static_fields.get(field_id).unwrap() - } else { - class.instance_fields.get(field_id).unwrap() - }; - let mut annotations = field.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annnotations set for a method (but not the parameters annotations). - fn insert_method_annotation_set( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let mut annotations = method.annotations.clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annotations set for a method parameter. - fn insert_parameters_annotation_set( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - parameter_idx: usize, - ) -> Result<()> { - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let mut annotations = method.parameters_annotations[parameter_idx].clone(); - - let mut annotation_set = AnnotationSetItem { entries: vec![] }; - annotations.sort_by_key(|annot| annot.annotation.type_.clone()); - for annot in annotations { - annotation_set.entries.push(AnnotationOffItem { - annotation_off: self - .section_manager - .get_aligned_size(Section::AnnotationItem), - }); // linked in link_annotations() - - let item = AnnotationItem { - visibility: match ( - annot.visibility_build, - annot.visibility_runtime, - annot.visibility_system, - ) { - (true, false, false) => AnnotationVisibility::Build, - (false, true, false) => AnnotationVisibility::Runtime, - (false, false, true) => AnnotationVisibility::System, - _ => bail!( - "Annotation need visibility set to one and only one of build, \ - runtime or system" - ), // TODO: check if this is true - }, - annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, - }; - self.section_manager - .add_elt(Section::AnnotationItem, Some(item.size())); - self.annotation_items.push(item); - } - self.section_manager - .add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); - self.annotation_set_items.push(annotation_set); - Ok(()) - } - - /// Insert the annotations set list for a method parameters. - fn insert_parameters_annotation_set_list( - &mut self, - method_id: &IdMethod, - is_direct_method: bool, - ) -> Result<()> { - let mut list = AnnotationSetRefList { list: vec![] }; - let (class, _) = self.class_defs.get(&method_id.class_).unwrap(); - let method = if is_direct_method { - class.direct_methods.get(method_id).unwrap() - } else { - class.virtual_methods.get(method_id).unwrap() - }; - let param_has_annotation: Vec<_> = method - .parameters_annotations - .iter() - .map(|annots| !annots.is_empty()) - .collect(); - for (param_idx, has_annotation) in param_has_annotation.into_iter().enumerate() { - list.list.push(AnnotationSetRefItem { - annotations_off: if has_annotation { - let annotation_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; - annotation_off + 1 - } else { - 0 - }, // linked in link_annotations() - }); - } - - self.section_manager - .add_elt(Section::AnnotationSetRefList, Some(list.size())); - self.annotation_set_lists.push(list); - Ok(()) - } - - /// Insert a class annotations (including field, methods and parameters annotations). - /// - /// # Note - /// - /// annotation_set_item objects are 4 bytes aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_annotations(&mut self, class_id: &IdType) -> Result<()> { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_annotations_off = if !class.annotations.is_empty() { - let class_annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem); - self.insert_class_annotation_set(class_id) - .with_context(|| { - format!( - "Failed to insert class annotation for class {}", - class_id.__repr__() - ) - })?; - class_annotations_off + 1 - } else { - 0 - }; - - let mut field_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - field_ids.extend(class.static_fields.keys().cloned()); - field_ids.extend(class.instance_fields.keys().cloned()); + type_list_set.extend(proto_set.iter().map(|proto| proto.parameters.clone())); + let mut strings: Vec = string_set.into_iter().collect(); + strings.sort(); + let mut type_ids: Vec = type_set.into_iter().collect(); + type_ids.sort(); + let mut proto_ids: Vec = proto_set.into_iter().collect(); + proto_ids.sort(); + let mut field_ids: Vec = field_set.into_iter().collect(); field_ids.sort(); - let mut field_annotations = vec![]; - for field_id in field_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_field = class.static_fields.get(&field_id); - let instance_field = class.instance_fields.get(&field_id); - let (is_static, field) = match (static_field, instance_field) { - (Some(field), None) => (true, field), - (None, Some(field)) => (false, field), - _ => bail!( - "Unexpected configuration: field {} is both a static and a instance field in {}", - field_id.__repr__(), class_id.__repr__()), - }; - if !field.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_field_annotation_set(&field_id, is_static)?; - field_annotations.push(FieldAnnotation { - field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( - "Field {} in {} not found in dex builder", - field_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let mut method_ids = vec![]; - let (class, _) = self.class_defs.get(class_id).unwrap(); - method_ids.extend(class.direct_methods.keys().cloned()); - method_ids.extend(class.virtual_methods.keys().cloned()); + let mut method_ids: Vec = method_set.into_iter().collect(); method_ids.sort(); - let mut method_annotations = vec![]; - for method_id in &method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(method_id); - let virtual_method = class.virtual_methods.get(method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetItem) - + 1; - self.insert_method_annotation_set(method_id, is_direct)?; - method_annotations.push(MethodAnnotation { - method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } + let mut type_lists: Vec> = type_list_set.into_iter().collect(); - let mut parameter_annotations = vec![]; - for method_id in method_ids { - let (class, _) = self.class_defs.get(class_id).unwrap(); - let direct_method = class.direct_methods.get(&method_id); - let virtual_method = class.virtual_methods.get(&method_id); - let (is_direct, method) = match (direct_method, virtual_method) { - (Some(method), None) => (true, method), - (None, Some(method)) => (false, method), - _ => bail!( - "Unexpected configuration: method {} is both a direct and a virtual method in {}", - method_id.__repr__(), class_id.__repr__()), - }; - if !method.parameters_annotations.is_empty() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationSetRefList) - + 1; - self.insert_parameters_annotation_set_list(&method_id, is_direct)?; - parameter_annotations.push(ParameterAnnotation { - method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( - "Method {} in {} not found in dex builder", - method_id.__repr__(), - class_id.__repr__(), - ))? as u32, - annotations_off, // linked in link_annotations() - }); - } - } - - let item = AnnotationsDirectoryItem { - class_annotations_off, // linked in link_annotations() - field_annotations, - method_annotations, - parameter_annotations, - }; - self.section_manager - .add_elt(Section::AnnotationsDirectoryItem, Some(item.size())); - self.annotations_directory_items.push(item); - Ok(()) - } - - /// Insert a class_def_item in the class_defs section **and** the other struct that needs to be - /// generated on the fly. - /// - /// # Warning - /// - /// The class_defs section **MUST** be sorted by inheritance dependencies (parents classes and - /// interfaces must appear **before** child classes). Accordingly, this method must be invoked - /// in the right order. - /// - /// # Note - /// - /// annotations_directory_item, encoded_array_item and class_data_item objects are 4 bytes - /// aligns, so their offset cannot be odd. - /// - /// To distinguish prelinked value (offset inside the code_item section) to actual values (offset - /// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow - /// to distinguish the offset of the first item (equal to zero before linking) and the value - /// 0 used to indicate an abscence of item. - fn insert_class_def_item(&mut self, class_id: &IdType) -> Result<()> { - let idx = self.class_defs_list.len(); - self.class_defs - .entry(class_id.clone()) - .and_modify(|(_, i)| *i = idx); - let (class, _) = self.class_defs.get(class_id).unwrap(); - let class_data_off = if class.has_data_item() { - let class_data_off = self - .section_manager - .get_aligned_size(Section::ClassDataItem); - self.insert_class_data_item(class_id)?; - class_data_off + 1 - } else { - 0 - }; - // & vs &mut cluster-f, this make rust drop the ref so self hold by `class` before - // mutating self with `insert_class_data_item`, and get a new ref afterward - let (class, _) = self.class_defs.get(class_id).unwrap(); - let static_values_off = if class.has_static_values_array() { - let static_values_off = self - .section_manager - .get_aligned_size(Section::EncodedArrayItem); - self.insert_class_static_values(class_id)?; - static_values_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - let annotations_off = if class.has_annotations() { - let annotations_off = self - .section_manager - .get_aligned_size(Section::AnnotationsDirectoryItem); - self.insert_annotations(class_id)?; - annotations_off + 1 - } else { - 0 - }; - let (class, _) = self.class_defs.get(class_id).unwrap(); - self.class_defs_list.push(ClassDefItem { - class_idx: *self.type_ids.get(class_id).ok_or(anyhow!( - "Type {} (type of class {}) not found in dex builder", - class_id.__repr__(), - class.__repr__() - ))? as u32, - access_flags: class.get_raw_access_flags(), - superclass_idx: if let Some(sup) = &class.superclass { - *self.type_ids.get(sup).ok_or(anyhow!( - "Type {} (superclass of class {}) not found in dex builder", - sup.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - interfaces_off: 0, - source_file_idx: if let Some(file) = &class.source_file { - *self.strings.get(file).ok_or(anyhow!( - "String {} (source file of class {}) not found in dex builder", - file.__repr__(), - class.__repr__() - ))? as u32 - } else { - NO_INDEX.0 - }, - - annotations_off, // need relinking once offset of class_def_item section is known - class_data_off, // need relinking once offset of class_data section is known - static_values_off, // need relinking once offset of encoded_array section is known - }); - self.section_manager.add_elt(Section::ClassDefItem, None); - Ok(()) - } - - fn gen_type_list_section(&mut self) -> Result<()> { - debug!("Generate the type_list section"); - // Collect all type lists - for proto in self.proto_ids.keys() { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - for (class, _) in self.class_defs.values() { - if !class.interfaces.is_empty() { - let type_list = self.gen_type_list(&class.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", class.__repr__()) - })?; - self.type_lists_index.insert(type_list, 0); - } - } - - // safe type lists with their offset in the section - let mut offset = 0; - for (i, (list, idx)) in self.type_lists_index.iter_mut().enumerate() { - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - *idx = i; - self.type_lists_with_offset.push((list.clone(), offset)); - self.section_manager - .add_elt(Section::TypeList, Some(list.size())); - offset += list.size() as u32; - } - // The next section requires alignment to 4 - while offset % 4 != 0 { - // Alignment - self.section_manager.incr_section_size(Section::TypeList, 1); - offset += 1; - } - Ok(()) - } - - /// Generate the map list. - /// - /// # Warning - /// - /// All sections must be generated (but not linked) before generating the map list. - /// - /// This method switch the section manager from edit mode to read only. - fn gen_map_list(&mut self) -> Result<()> { - debug!("Generate the map_list"); - // Get the size of a map item - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - // Empty map has a size 4, then we add the size of a MapItem for each element - // The size of the map_list must be computed before generating the map list, - // as it affect the offset of some sections. - self.section_manager.add_elt(Section::MapList, Some(4)); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - self.section_manager - .incr_section_size(Section::MapList, map_item_size); - } - } - // All sections are knowns and should not be eddited anymore - self.section_manager.finalize_sections(); - for section in Section::VARIANT_LIST { - if !section.is_data() && self.section_manager.get_nb_elt(*section) != 0 { - /* - match section { - // Alignment - // Until Section::MapList included, the section are naturally alligned to 4 - _ => (), - } - */ - self.map_list.list.push(MapItem { - type_: section.get_map_item_type(), - unused: 0, - size: self.section_manager.get_nb_elt(*section) as u32, - offset: self.section_manager.get_offset(*section), - }); - } - } - Ok(()) - } - - /// Link the offsets in the header. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_header(&mut self) { - debug!("Link the header section"); - self.header.map_off = self.section_manager.get_offset(Section::MapList); - self.header.string_ids_size = self.section_manager.get_nb_elt(Section::StringIdItem) as u32; - self.header.string_ids_off = self.section_manager.get_offset(Section::StringIdItem); - self.header.type_ids_size = self.section_manager.get_nb_elt(Section::TypeIdItem) as u32; - self.header.type_ids_off = self.section_manager.get_offset(Section::TypeIdItem); - self.header.proto_ids_size = self.section_manager.get_nb_elt(Section::ProtoIdItem) as u32; - self.header.proto_ids_off = self.section_manager.get_offset(Section::ProtoIdItem); - self.header.field_ids_size = self.section_manager.get_nb_elt(Section::FieldIdItem) as u32; - self.header.field_ids_off = self.section_manager.get_offset(Section::FieldIdItem); - self.header.method_ids_size = self.section_manager.get_nb_elt(Section::MethodIdItem) as u32; - self.header.method_ids_off = self.section_manager.get_offset(Section::MethodIdItem); - self.header.class_defs_size = self.section_manager.get_nb_elt(Section::ClassDefItem) as u32; - self.header.class_defs_off = self.section_manager.get_offset(Section::ClassDefItem); - self.header.data_size = self.section_manager.get_unaligned_size(Section::Data); - self.header.data_off = self.section_manager.get_offset(Section::Data); - } - - /// Link the offsets in the call site id items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_call_site_ids(&mut self) { - debug!("Link call site id items"); - for id in &mut self.call_site_ids { - id.call_site_off += self.section_manager.get_offset(Section::EncodedArrayItem); - } - } - - /// Link the offsets in class_def_items. - /// - /// # Warning - /// - /// This is the only link method called before generating the map list and finilizing the - /// section: - /// - /// Linking can only occur once all sections are entirelly generated, however, - /// `class_data_item.direct|virtual_methods[.].code_off` are Uleb128 encoded, meaning - /// that linking class_data_item modify the size of the class_data_items, hence the position - /// of the class_data_item and all element located after, as well as the size of the data - /// section. This is pretty bothersome and means that the sections **are** modified. - fn link_class_data(&mut self) -> Result<()> { - debug!("Link class data items"); - let mut unlinked_local_offset = 0; - let mut linked_local_offset = 0; - let code_section_off = self.section_manager.get_code_item_offset_prefinalized(); - for data in self.class_data_list.iter_mut() { - let unlinked_size = data.size() as u32; - for method in &mut data.direct_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - for method in &mut data.virtual_methods { - if method.code_off.0 != 0 { - method.code_off.0 += code_section_off - 1; - } - } - self.corrected_class_data_offset - .insert(unlinked_local_offset, linked_local_offset); - linked_local_offset += data.size() as u32; - unlinked_local_offset += unlinked_size; - } - self.section_manager.incr_section_size( - Section::ClassDataItem, - linked_local_offset as usize - unlinked_local_offset as usize, - ); - Ok(()) - } - - /// Link the offsets in proto_id_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_proto_id(&mut self) -> Result<()> { - debug!("Link proto id items"); - for (proto, idx) in &self.proto_ids { - if !proto.parameters.is_empty() { - let type_list = self.gen_type_list(&proto.parameters).with_context(|| { - format!("Failed to generate param list for {}", proto.__repr__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.proto_ids_list[*idx].parameters_off = offset; - } - } - Ok(()) - } - - /// Link the offsets of class_data_items in class_def_items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_class_def(&mut self) -> Result<()> { - debug!("Link class_def_items"); - for class_def in self.class_defs_list.iter_mut() { - // Link the class_data_item entries - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.class_data_off != 0 { - let unlinked_local_offset = class_def.class_data_off - 1; - let linked_local_offset = *self - .corrected_class_data_offset - .get(&unlinked_local_offset) - .expect( - "Unlinked class_data_item offset not found in corrected_class_data_offset", - ); - class_def.class_data_off = - self.section_manager.get_offset(Section::ClassDataItem) + linked_local_offset; - } - // Link the annotations_directory_item entrie - // prelink value is set to offset in the section + 1 (to distinguish with 0) - if class_def.annotations_off != 0 { - class_def.annotations_off += self - .section_manager - .get_offset(Section::AnnotationsDirectoryItem) - - 1; - } - - // Link the static_values entries - if class_def.static_values_off != 0 { - class_def.static_values_off += - self.section_manager.get_offset(Section::EncodedArrayItem) - 1; - } - } - for (cls, idx) in self.class_defs.values() { - if !cls.interfaces.is_empty() { - let type_list = self.gen_type_list(&cls.interfaces).with_context(|| { - format!("Failed to generate interface list for {}", cls.__repr__()) - })?; - let offset = self.section_manager.get_offset(Section::TypeList) - + self.type_lists_with_offset[*self.type_lists_index.get(&type_list).unwrap()] - .1; - self.class_defs_list[*idx].interfaces_off = offset; - } - } - Ok(()) - } - - /// Link the offset of debug info item in code items. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_code(&mut self) { - debug!("Link the debug_info_off entries in code_items"); - for code in self.code_items.iter_mut() { - if code.debug_info_off != 0 { - code.debug_info_off += self.section_manager.get_offset(Section::DebugInfoItem) - 1; - } - } - } - - /// Link all annotations objects. - /// - /// # Warning - /// - /// Linking can only occur once all sections are entirelly generated. - fn link_annotations(&mut self) { - for annotation in self.annotations_directory_items.iter_mut() { - if annotation.class_annotations_off != 0 { - annotation.class_annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - for field_annotation in annotation.field_annotations.iter_mut() { - if field_annotation.annotations_off != 0 { - field_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for method_annotation in annotation.method_annotations.iter_mut() { - if method_annotation.annotations_off != 0 { - method_annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - for parameter_annotation in annotation.parameter_annotations.iter_mut() { - if parameter_annotation.annotations_off != 0 { - parameter_annotation.annotations_off += self - .section_manager - .get_offset(Section::AnnotationSetRefList) - - 1; - } - } - } - for annotation_set in self.annotation_set_items.iter_mut() { - for entry in annotation_set.entries.iter_mut() { - entry.annotation_off += self.section_manager.get_offset(Section::AnnotationItem); - // -1 now when linking frag - } - } - for list in self.annotation_set_lists.iter_mut() { - for annotation in list.list.iter_mut() { - if annotation.annotations_off != 0 { - annotation.annotations_off += - self.section_manager.get_offset(Section::AnnotationSetItem) - 1; - } - } - } - } - - fn write_dex_file(&mut self, writer: &mut dyn Write) -> Result<()> { - self.section_manager.reset(); - self.section_manager.add_elt(Section::HeaderItem, None); - - self.gen_string_data_section()?; - self.gen_type_ids_section()?; - self.gen_proto_ids_section()?; - self.gen_field_ids_section()?; - self.gen_method_ids_section()?; - - debug!("Sort classes and generate the class_defs and class_data section"); - for class_id in self.get_sorted_class_def()? { - self.insert_class_def_item(&class_id)?; - } - self.gen_type_list_section()?; - - // start by linking class_data_items to populate self.corrected_class_data_offset - // and update the class_data_item sections size. - // Why before gen_map_list? Because the offsets in class_data_items are F***ing Uleb128 - // encoded, so there size change when linking (see doc of self.corrected_class_data_offset). - let code_offset = self.section_manager.get_code_item_offset_prefinalized(); - self.link_class_data()?; - self.gen_map_list()?; - assert_eq!( - code_offset, - self.section_manager.get_offset(Section::CodeItem), - "Prelinking computed value and post linking value for \ - the offset of the code_item section don't match" - ); - - // From now on, all section are generated and the value in section_manager do not change, - - self.link_header(); - self.link_call_site_ids(); - self.link_proto_id()?; - self.link_class_def()?; - self.link_code(); - self.link_annotations(); - - debug!("Serialize the dex file"); - let mut buffer = Cursor::new(Vec::::new()); - - self.check_section_offset(&buffer, Section::HeaderItem); - Self::fix_section_alignement(&mut buffer, Section::HeaderItem)?; - self.header.serialize(&mut buffer)?; - // StringIdItem section - let mut string_off = self.section_manager.get_offset(Section::StringDataItem); - self.check_section_offset(&buffer, Section::StringIdItem); - for string in self.string_data_list.iter() { - let str_id = StringIdItem { - string_data_off: string_off, - }; - Self::fix_section_alignement(&mut buffer, Section::StringIdItem)?; - str_id.serialize(&mut buffer)?; - string_off += string.size() as u32; - } - // TypeId section - self.check_section_offset(&buffer, Section::TypeIdItem); - for ty in &self.type_ids_list { - Self::fix_section_alignement(&mut buffer, Section::TypeIdItem)?; - ty.serialize(&mut buffer)?; - } - // ProtoId section - self.check_section_offset(&buffer, Section::ProtoIdItem); - for proto in &self.proto_ids_list { - Self::fix_section_alignement(&mut buffer, Section::ProtoIdItem)?; - proto.serialize(&mut buffer)?; - } - // FieldIdItem section - self.check_section_offset(&buffer, Section::FieldIdItem); - for field_id in &self.field_ids_list { - Self::fix_section_alignement(&mut buffer, Section::FieldIdItem)?; - field_id.serialize(&mut buffer)?; - } - // MethodIdItem section - self.check_section_offset(&buffer, Section::MethodIdItem); - for method_id in &self.method_ids_list { - Self::fix_section_alignement(&mut buffer, Section::MethodIdItem)?; - method_id.serialize(&mut buffer)?; - } - // ClassDefItem section - self.check_section_offset(&buffer, Section::ClassDefItem); - for class_def in &self.class_defs_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDefItem)?; - class_def.serialize(&mut buffer)?; - } - // CallSiteIdItem, data are inserted as encoded array item later - self.check_section_offset(&buffer, Section::CallSiteIdItem); - for call_site_id in &self.call_site_ids { - Self::fix_section_alignement(&mut buffer, Section::CallSiteIdItem)?; - call_site_id.serialize(&mut buffer)?; - } - - // MethodHandleItem section - self.check_section_offset(&buffer, Section::MethodHandleItem); - for handle in &self.method_handles { - Self::fix_section_alignement(&mut buffer, Section::MethodHandleItem)?; - handle.serialize(&mut buffer)?; - } - // MapList - self.check_section_offset(&buffer, Section::Data); - self.check_section_offset(&buffer, Section::MapList); - Self::fix_section_alignement(&mut buffer, Section::MapList)?; - self.map_list.serialize(&mut buffer)?; - // TypeList, - self.check_section_offset(&buffer, Section::TypeList); - for (list, _) in &self.type_lists_with_offset { - Self::fix_section_alignement(&mut buffer, Section::TypeList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetRefList section - self.check_section_offset(&buffer, Section::AnnotationSetRefList); - for list in &self.annotation_set_lists { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetRefList)?; - list.serialize(&mut buffer)?; - } - // AnnotationSetItem section - self.check_section_offset(&buffer, Section::AnnotationSetItem); - for set in &self.annotation_set_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationSetItem)?; - set.serialize(&mut buffer)?; - } - // CodeItem section - self.check_section_offset(&buffer, Section::CodeItem); - for code_item in &self.code_items { - Self::fix_section_alignement(&mut buffer, Section::CodeItem)?; - code_item.serialize(&mut buffer)? - } - // StringDataItem section - self.check_section_offset(&buffer, Section::StringDataItem); - for string in &self.string_data_list { - Self::fix_section_alignement(&mut buffer, Section::StringDataItem)?; - string.serialize(&mut buffer)?; - } - // DebugInfoItem section - self.check_section_offset(&buffer, Section::DebugInfoItem); - for debug_info in &self.debug_info_items { - Self::fix_section_alignement(&mut buffer, Section::DebugInfoItem)?; - debug_info.serialize(&mut buffer)?; - } - // AnnotationItem section - self.check_section_offset(&buffer, Section::AnnotationItem); - for annot in &self.annotation_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationItem)?; - annot.serialize(&mut buffer)?; - } - // EncodedArrayItem section - self.check_section_offset(&buffer, Section::EncodedArrayItem); - for array in &self.encoded_array_items { - Self::fix_section_alignement(&mut buffer, Section::EncodedArrayItem)?; - array.serialize(&mut buffer)?; - } - // AnnotationsDirectoryItem section - self.check_section_offset(&buffer, Section::AnnotationsDirectoryItem); - for dir in &self.annotations_directory_items { - Self::fix_section_alignement(&mut buffer, Section::AnnotationsDirectoryItem)?; - dir.serialize(&mut buffer)?; - } - // ClassDataItem section - self.check_section_offset(&buffer, Section::ClassDataItem); - for data in &self.class_data_list { - Self::fix_section_alignement(&mut buffer, Section::ClassDataItem)?; - data.serialize(&mut buffer)?; - } - // TODO: HiddenapiClassDataItem, - /* - self.check_section_offset(&buffer, Section::HiddenapiClassDataItem); - Self::fix_section_alignement(&mut buffer, Section::HiddenapiClassDataItem)?; - */ - - let end_data = buffer.position(); - assert_eq!( - end_data as u32, - self.header.data_off + self.header.data_size - ); - - // compute signature - buffer.seek(SeekFrom::Start(8 + 4 + 20))?; - let mut hasher = Sha1::new(); - io::copy(&mut buffer, &mut hasher)?; - self.header.signature = hasher.finalize().into(); - let size = buffer.seek(SeekFrom::End(0))? as u32; - self.header.file_size = size; - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // Compute checksum - //buffer.seek(SeekFrom::Start(8 + 4))?; - let mut adler = Adler32::new(); - adler.write_slice(&buffer.get_ref()[8 + 4..]); - self.header.checksum = adler.checksum(); - buffer.rewind()?; - self.header.serialize(&mut buffer)?; - - // copy buffer to output - buffer.rewind()?; - io::copy(&mut buffer, writer)?; - - Ok(()) - } - - /// Insert 0 to a buffer until the right alignment is reached for an element of the - /// given section. - fn fix_section_alignement(buffer: &mut Cursor>, section: Section) -> Result<()> { - while buffer.position() % section.get_item_alignment() as u64 != 0 { - Serializable::serialize(&0u8, buffer)?; - } - Ok(()) - } - - /// Check if a section - fn check_section_offset(&self, buffer: &Cursor, section: Section) { - let mut pos = buffer.position(); - while pos % section.get_item_alignment() as u64 != 0 { - pos += 1; - } - let expected = self.section_manager.get_offset(section) as u64; - assert_eq!( - pos, expected, - "Computed section offset and actual section offset do not match for section \ - {section:?}, expected 0x{expected:x}, found 0x{pos:x}" - ); - } - - /// Compute the order of the classes in the section `class_defs`. - /// Class definitions must be sorted so that a class's superclass and interfaces - /// are before the class. - fn get_sorted_class_def(&self) -> Result> { - // Use Kahn's algorithm - let mut graph: HashMap<&IdType, (HashSet<&IdType>, HashSet<&IdType>)> = HashMap::new(); - for (ty, (def, _)) in &self.class_defs { - let mut edges_to = HashSet::new(); - if let Some(sup) = def.superclass.as_ref() { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for sup in &def.interfaces { - if self.class_defs.get(sup).is_some() { - edges_to.insert(sup); - } - } - for n_to in &edges_to { - let (from, _) = graph - .entry(n_to) - .or_insert((HashSet::new(), HashSet::new())); - from.insert(ty); - } - let (_, to) = graph.entry(ty).or_insert((HashSet::new(), HashSet::new())); - to.extend(edges_to); - } - - let mut sorted = vec![]; - let mut no_outgoing: VecDeque<&IdType> = VecDeque::new(); - no_outgoing.extend( - graph - .iter() - .filter(|(_, (_, to))| to.is_empty()) - .map(|(ty, _)| ty), - ); - if no_outgoing.is_empty() { - bail!("The class inheritance topoloy is either empty or cyclic"); - } - - while let Some(n) = no_outgoing.pop_front() { - sorted.push(n.clone()); - let (from, _) = graph.get(n).cloned().unwrap(); - for n_from in from { - graph.entry(n_from).and_modify(|(_, to)| _ = to.remove(n)); - let (_, to) = graph.get(n_from).unwrap(); - if to.is_empty() { - no_outgoing.push_back(n_from); - } - } - graph - .entry(n) - .and_modify(|(from, _)| *from = HashSet::new()); - } - for (_, (from, to)) in graph { - if !from.is_empty() || !to.is_empty() { - bail!("The class inheritance topology is cyclic"); - } - } - Ok(sorted) - } - - fn gen_type_list(&self, list: &[IdType]) -> Result { - let mut type_list = TypeList { list: vec![] }; - for ty in list { - type_list.list.push(TypeItem { - type_idx: *self.type_ids.get(ty).ok_or(anyhow!( - "Could not found type {} in dex builder", - ty.__repr__() - ))? as u16, - }); - } - Ok(type_list) - } - - pub fn gen_dex_file_to_vec(&mut self) -> Result> { - let mut output = Cursor::new(Vec::::new()); - self.write_dex_file(&mut output)?; - Ok(output.into_inner()) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Section { - HeaderItem, - StringIdItem, - TypeIdItem, - ProtoIdItem, - FieldIdItem, - MethodIdItem, - ClassDefItem, - CallSiteIdItem, - MethodHandleItem, - Data, - MapList, - TypeList, - AnnotationSetRefList, - AnnotationSetItem, - CodeItem, - StringDataItem, - DebugInfoItem, - AnnotationItem, - EncodedArrayItem, - AnnotationsDirectoryItem, - HiddenapiClassDataItem, - ClassDataItem, -} - -impl Section { - const VARIANT_LIST: &'static [Self] = &[ - Self::HeaderItem, - Self::StringIdItem, - Self::TypeIdItem, - Self::ProtoIdItem, - Self::FieldIdItem, - Self::MethodIdItem, - Self::ClassDefItem, - Self::CallSiteIdItem, - Self::MethodHandleItem, - Self::Data, - Self::MapList, - Self::TypeList, - Self::AnnotationSetRefList, - Self::AnnotationSetItem, - Self::CodeItem, - Self::StringDataItem, - Self::DebugInfoItem, - Self::AnnotationItem, - Self::EncodedArrayItem, - Self::AnnotationsDirectoryItem, - Self::ClassDataItem, // must be last because contains offsets in Uleb, - // so size change when linking ! - Self::HiddenapiClassDataItem, - ]; - - fn get_index(&self) -> usize { - match self { - Self::HeaderItem => 0, - Self::StringIdItem => 1, - Self::TypeIdItem => 2, - Self::ProtoIdItem => 3, - Self::FieldIdItem => 4, - Self::MethodIdItem => 5, - Self::ClassDefItem => 6, - Self::CallSiteIdItem => 7, - Self::MethodHandleItem => 8, - Self::Data => 9, - Self::MapList => 10, - Self::TypeList => 11, - Self::AnnotationSetRefList => 12, - Self::AnnotationSetItem => 13, - Self::CodeItem => 14, - Self::StringDataItem => 15, - Self::DebugInfoItem => 16, - Self::AnnotationItem => 17, - Self::EncodedArrayItem => 18, - Self::AnnotationsDirectoryItem => 19, - Self::ClassDataItem => 20, - Self::HiddenapiClassDataItem => 21, - } - } - - fn get_elt_size(&self, default_size: Option) -> usize { - let fixed_size = match self { - Self::HeaderItem => Some(0x70), - Self::StringIdItem => Some(4), - Self::TypeIdItem => Some(4), - Self::ProtoIdItem => Some(0xc), - Self::FieldIdItem => Some(8), - Self::MethodIdItem => Some(8), - Self::ClassDefItem => Some(0x20), - Self::CallSiteIdItem => Some(4), - Self::MethodHandleItem => Some(8), - Self::Data => panic!("Element cannot be inserted in data dirctly"), - Self::MapList => None, - Self::TypeList => None, - Self::AnnotationSetRefList => None, - Self::AnnotationSetItem => None, - Self::ClassDataItem => None, - Self::CodeItem => None, - Self::StringDataItem => None, - Self::DebugInfoItem => None, - Self::AnnotationItem => None, - Self::EncodedArrayItem => None, - Self::AnnotationsDirectoryItem => None, - Self::HiddenapiClassDataItem => None, - }; - if let (Some(fixed_size), Some(default_size)) = (fixed_size, default_size) { - if fixed_size == default_size { - default_size - } else { - panic!( - "Element in {:?} have a size of {}, not {}", - self, fixed_size, default_size - ) - } - } else { - fixed_size.or(default_size).unwrap_or_else(|| { - panic!( - "Element of {:?} don't have a fixed size, you need to provide one", - self - ) - }) - } - } - - /// Return the previous section if it exist. - fn prev(&self) -> Option { - match self { - Self::HeaderItem => None, - Self::StringIdItem => Some(Self::HeaderItem), - Self::TypeIdItem => Some(Self::StringIdItem), - Self::ProtoIdItem => Some(Self::TypeIdItem), - Self::FieldIdItem => Some(Self::ProtoIdItem), - Self::MethodIdItem => Some(Self::FieldIdItem), - Self::ClassDefItem => Some(Self::MethodIdItem), - Self::CallSiteIdItem => Some(Self::ClassDefItem), - Self::MethodHandleItem => Some(Self::CallSiteIdItem), - Self::Data => Some(Self::MethodHandleItem), - Self::MapList => Some(Self::MethodHandleItem), // Data is just an indicator - Self::TypeList => Some(Self::MapList), - Self::AnnotationSetRefList => Some(Self::TypeList), - Self::AnnotationSetItem => Some(Self::AnnotationSetRefList), - Self::CodeItem => Some(Self::AnnotationSetItem), - Self::StringDataItem => Some(Self::CodeItem), - Self::DebugInfoItem => Some(Self::StringDataItem), - Self::AnnotationItem => Some(Self::DebugInfoItem), - Self::EncodedArrayItem => Some(Self::AnnotationItem), - Self::AnnotationsDirectoryItem => Some(Self::EncodedArrayItem), - Self::ClassDataItem => Some(Self::AnnotationsDirectoryItem), - Self::HiddenapiClassDataItem => Some(Self::ClassDataItem), - } - } - - fn get_map_item_type(&self) -> MapItemType { - match self { - Self::HeaderItem => MapItemType::HeaderItem, - Self::StringIdItem => MapItemType::StringIdItem, - Self::TypeIdItem => MapItemType::TypeIdItem, - Self::ProtoIdItem => MapItemType::ProtoIdItem, - Self::FieldIdItem => MapItemType::FieldIdItem, - Self::MethodIdItem => MapItemType::MethodIdItem, - Self::ClassDefItem => MapItemType::ClassDefItem, - Self::CallSiteIdItem => MapItemType::CallSiteIdItem, - Self::MethodHandleItem => MapItemType::MethodHandleItem, - Self::Data => panic!("Data is not a MatItemType"), - Self::MapList => MapItemType::MapList, - Self::TypeList => MapItemType::TypeList, - Self::AnnotationSetRefList => MapItemType::AnnotationSetRefList, - Self::AnnotationSetItem => MapItemType::AnnotationSetItem, - Self::CodeItem => MapItemType::CodeItem, - Self::StringDataItem => MapItemType::StringDataItem, - Self::DebugInfoItem => MapItemType::DebugInfoItem, - Self::AnnotationItem => MapItemType::AnnotationItem, - Self::EncodedArrayItem => MapItemType::EncodedArrayItem, - Self::AnnotationsDirectoryItem => MapItemType::AnnotationsDirectoryItem, - Self::ClassDataItem => MapItemType::ClassDataItem, - Self::HiddenapiClassDataItem => MapItemType::HiddenapiClassDataItem, - } - } - - /// Return the alignment of the item in byte. - fn get_item_alignment(&self) -> u32 { - match self { - Self::HeaderItem => 4, - Self::StringIdItem => 4, - Self::TypeIdItem => 4, - Self::ProtoIdItem => 4, - Self::FieldIdItem => 4, - Self::MethodIdItem => 4, - Self::ClassDefItem => 4, - Self::CallSiteIdItem => 1, - Self::MethodHandleItem => 4, - Self::Data => 1, - Self::MapList => 4, - Self::TypeList => 4, - Self::AnnotationSetRefList => 4, - Self::AnnotationSetItem => 4, - Self::CodeItem => 4, - Self::StringDataItem => 1, - Self::DebugInfoItem => 1, - Self::AnnotationItem => 1, - Self::EncodedArrayItem => 1, - Self::AnnotationsDirectoryItem => 4, - Self::ClassDataItem => 1, - Self::HiddenapiClassDataItem => 1, - } - } - - fn is_data(&self) -> bool { - matches!(self, Self::Data) - } -} - -#[derive(Debug, Default, Clone)] -struct SectionManager { - sizes: [u32; Self::NB_SECTION], - nb_elt: [usize; Self::NB_SECTION], - offsets: [u32; Self::NB_SECTION], - editable: bool, -} - -impl SectionManager { - const NB_SECTION: usize = 22; - - fn reset(&mut self) { - self.sizes = [0; Self::NB_SECTION]; - self.nb_elt = [0; Self::NB_SECTION]; - self.offsets = [0; Self::NB_SECTION]; - self.editable = true; - } - - fn add_elt(&mut self, section: Section, size: Option) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } - if section.is_data() { - panic!("Cannot add element directly in section data"); - } - while self.sizes[section.get_index()] % section.get_item_alignment() != 0 { - self.sizes[section.get_index()] += 1; - } - self.sizes[section.get_index()] += section.get_elt_size(size) as u32; - self.nb_elt[section.get_index()] += 1; - } - - fn incr_section_size(&mut self, section: Section, size: usize) { - if !self.editable { - panic!("Try to modify a section when the sections are set to read only"); - } - self.sizes[section.get_index()] += size as u32; - } - - fn get_offset(&self, section: Section) -> u32 { - if self.editable { - panic!("Try to get section offset before sections are finilized"); - } - let size = self.offsets[section.get_index()]; - let alignment = section.get_item_alignment(); - if size % alignment != 0 { - panic!( - "section {section:?} should be aligned on {alignment} bytes, \ - found section offset 0x{size:x}" - ); // avoid by finilized - } - size - } - - fn get_unaligned_size(&self, section: Section) -> u32 { - if section.is_data() { - self.sizes[section.get_index()..].iter().sum() - } else { - self.sizes[section.get_index()] - } - } - - /// The position of a potential new item in the section considering alignment. - fn get_aligned_size(&self, section: Section) -> u32 { - let mut size = self.get_unaligned_size(section); - while size % section.get_item_alignment() != 0 { - size += 1; - } - size - } - - fn get_nb_elt(&self, section: Section) -> usize { - self.nb_elt[section.get_index()] - } - - /// Finialize the sections: switch to read only and fix the section alignment. - fn finalize_sections(&mut self) { - for section in Section::VARIANT_LIST { - while self.sizes[..section.get_index()].iter().sum::() - % section.get_item_alignment() - != 0 - { - self.incr_section_size( - section.prev().expect( - "First section (Header) should alway be aligned but \ - found unaligned section without predecessor", - ), - 1, - ); - } - } - let mut offset = 0; - for section in Section::VARIANT_LIST { - self.offsets[section.get_index()] = offset; - offset += self.sizes[section.get_index()]; - } - - self.editable = false; - } - - /// This method exist for the only purpose of linking the method code offset inside - /// the class data items. This linking needs to be done before finilizing because it change the - /// size of the class data item section. - /// - /// Seriously, avoid using this. - fn get_code_item_offset_prefinalized(&mut self) -> u32 { - if !self.editable || self.get_nb_elt(Section::MapList) != 0 { - panic!("Don't use this method for other purpose than linking class_data_items"); - } - let mut map_list_size = 4; - let map_item_size = 12; /* = MapItem { - type_: MapItemType::HeaderItem, - unused: 0, - size: 0, - offset: 0, - } - .size(); */ - for section in Section::VARIANT_LIST { - if !section.is_data() - && (self.get_nb_elt(*section) != 0 || section == &Section::MapList) - { - map_list_size += map_item_size; - } - } - let mut offset = map_list_size; // This is aligned so it wont affect alignment - for section in &Section::VARIANT_LIST[..Section::CodeItem.get_index()] { - // size Section::Data and size Section::MapList are 0 - while offset % section.get_item_alignment() != 0 { - offset += 1; - } - offset += self.sizes[section.get_index()]; - } - - offset - } - - /// Display the sections informations. - #[allow(dead_code)] - fn show(&self) { - let mut offset = 0; - for section in Section::VARIANT_LIST { - let size = self.get_unaligned_size(*section); - let new_offset = offset + size; - let nb_elt = self.get_nb_elt(*section); - println!( - "{section:?}: 0x{offset:x} -> 0x{new_offset:x} (size: 0x{size:x}, \ - nb elt: {nb_elt})" + for (i, fragment) in fragments.iter().enumerate() { + fragment.link_global_ids( + &strings, + &type_ids, + &proto_ids, + &field_ids, + &method_ids, + nb_method_handle_before[i], ); - if !section.is_data() { - offset = new_offset; - } } + + vec![] } }