This commit is contained in:
Jean-Marie Mineau 2024-03-21 14:20:44 +01:00
parent d28968c2e3
commit 609e6c69cd
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 336 additions and 3243 deletions

View file

@ -45,12 +45,6 @@ pub struct DexFragment {
class_def: ClassDefItem, class_def: ClassDefItem,
/// The class_data. /// The class_data.
class_data: Option<ClassDataItem>, class_data: Option<ClassDataItem>,
// 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<TypeList, usize>,
// /// 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. /// The encoded_array_items section.
encoded_array_items: Vec<EncodedArrayItem>, encoded_array_items: Vec<EncodedArrayItem>,
/// The annotations_directory_item. /// The annotations_directory_item.
@ -69,9 +63,40 @@ pub struct DexFragment {
debug_info_items: Vec<DebugInfoItem>, debug_info_items: Vec<DebugInfoItem>,
/// The list of interfaces of the class. /// The list of interfaces of the class.
interfaces: Vec<IdType>, interfaces: Vec<IdType>,
/// The current link state of the fragment.
link_state: FragLinkState,
} }
impl DexFragment { 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<Self> { pub fn new(class: &Class) -> Result<Self> {
debug!( debug!(
"Building dex fragment for class {}", "Building dex fragment for class {}",
@ -108,6 +133,7 @@ impl DexFragment {
annotation_set_lists: vec![], annotation_set_lists: vec![],
debug_info_items: vec![], debug_info_items: vec![],
interfaces: class.interfaces.clone(), interfaces: class.interfaces.clone(),
link_state: FragLinkState::Unlinked,
}; };
frag.strings = class.get_all_strings().into_iter().collect(); frag.strings = class.get_all_strings().into_iter().collect();
frag.strings.sort(); frag.strings.sort();
@ -1852,6 +1878,168 @@ impl DexFragment {
self.annotation_set_lists.push(list); self.annotation_set_lists.push(list);
Ok(()) 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -1859,7 +2047,6 @@ enum FragSection {
ClassDefItem, ClassDefItem,
CallSiteIdItem, CallSiteIdItem,
MethodHandleItem, MethodHandleItem,
TypeList,
AnnotationSetRefList, AnnotationSetRefList,
AnnotationSetItem, AnnotationSetItem,
CodeItem, CodeItem,
@ -1877,7 +2064,6 @@ impl FragSection {
Self::ClassDefItem, Self::ClassDefItem,
Self::CallSiteIdItem, Self::CallSiteIdItem,
Self::MethodHandleItem, Self::MethodHandleItem,
Self::TypeList,
Self::AnnotationSetRefList, Self::AnnotationSetRefList,
Self::AnnotationSetItem, Self::AnnotationSetItem,
Self::CodeItem, Self::CodeItem,
@ -1886,8 +2072,7 @@ impl FragSection {
Self::AnnotationItem, Self::AnnotationItem,
Self::EncodedArrayItem, Self::EncodedArrayItem,
Self::AnnotationsDirectoryItem, Self::AnnotationsDirectoryItem,
// must be last because contains offsets in Uleb, // /!\ contains offsets in Uleb, so size change when linking !
// so size change when linking !
Self::ClassDataItem, Self::ClassDataItem,
// Self::HiddenapiClassDataItem, // Self::HiddenapiClassDataItem,
]; ];
@ -1897,17 +2082,16 @@ impl FragSection {
Self::ClassDefItem => 0, Self::ClassDefItem => 0,
Self::CallSiteIdItem => 1, Self::CallSiteIdItem => 1,
Self::MethodHandleItem => 2, Self::MethodHandleItem => 2,
Self::TypeList => 3, Self::AnnotationSetRefList => 3,
Self::AnnotationSetRefList => 4, Self::AnnotationSetItem => 4,
Self::AnnotationSetItem => 5, Self::CodeItem => 5,
Self::CodeItem => 6, Self::StringDataItem => 6,
Self::StringDataItem => 7, Self::DebugInfoItem => 7,
Self::DebugInfoItem => 8, Self::AnnotationItem => 8,
Self::AnnotationItem => 9, Self::EncodedArrayItem => 9,
Self::EncodedArrayItem => 10, Self::AnnotationsDirectoryItem => 10,
Self::AnnotationsDirectoryItem => 11, Self::ClassDataItem => 11,
Self::ClassDataItem => 12, // Self::HiddenapiClassDataItem => 12,
// Self::HiddenapiClassDataItem => 13,
} }
} }
@ -1916,7 +2100,6 @@ impl FragSection {
Self::ClassDefItem => Some(0x20), Self::ClassDefItem => Some(0x20),
Self::CallSiteIdItem => Some(4), Self::CallSiteIdItem => Some(4),
Self::MethodHandleItem => Some(8), Self::MethodHandleItem => Some(8),
Self::TypeList => None,
Self::AnnotationSetRefList => None, Self::AnnotationSetRefList => None,
Self::AnnotationSetItem => None, Self::AnnotationSetItem => None,
Self::CodeItem => None, Self::CodeItem => None,
@ -1953,7 +2136,6 @@ impl FragSection {
Self::ClassDefItem => 4, Self::ClassDefItem => 4,
Self::CallSiteIdItem => 1, Self::CallSiteIdItem => 1,
Self::MethodHandleItem => 4, Self::MethodHandleItem => 4,
Self::TypeList => 4,
Self::AnnotationSetRefList => 4, Self::AnnotationSetRefList => 4,
Self::AnnotationSetItem => 4, Self::AnnotationSetItem => 4,
Self::CodeItem => 4, Self::CodeItem => 4,
@ -2124,6 +2306,7 @@ impl FragSectionManager {
} }
/// Index that associate a type to its local id in a fragment. /// Index that associate a type to its local id in a fragment.
#[derive(Debug, Clone)]
struct FragIndex { struct FragIndex {
pub strings: HashMap<DexString, usize>, pub strings: HashMap<DexString, usize>,
pub types: HashMap<IdType, usize>, pub types: HashMap<IdType, usize>,
@ -2131,3 +2314,38 @@ struct FragIndex {
pub fields: HashMap<IdField, usize>, pub fields: HashMap<IdField, usize>,
pub methods: HashMap<IdMethod, usize>, pub methods: HashMap<IdMethod, usize>,
} }
/// 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<u32, u32>,
debug_info_item_relocation: HashMap<u32, u32>,
annotation_item_relocation: HashMap<u32, u32>,
encoded_array_relocation: HashMap<u32, u32>, // TODO only one?
},
}
impl FragLinkState {
fn start_linking_idx(&mut self) -> Result<()> {
match self {
Self::Unlinked => {
*self = Self::LinkedIdx {
code_item_relocation: HashMap::<u32, u32>::new(),
debug_info_item_relocation: HashMap::<u32, u32>::new(),
annotation_item_relocation: HashMap::<u32, u32>::new(),
encoded_array_relocation: HashMap::<u32, u32>::new(),
};
Ok(())
}
_ => {
bail!("Cannot link the idx in a fragment that is already linked");
}
}
}
}

File diff suppressed because it is too large Load diff