insert annotation in frag

This commit is contained in:
Jean-Marie Mineau 2024-03-13 17:16:44 +01:00
parent bfa957ad45
commit 3a2f45b28b
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 262 additions and 268 deletions

View file

@ -5,4 +5,7 @@
- no nop when no payload - no nop when no payload
- option to get label at every code addresses - option to get label at every code addresses
- name register / parameters - name register / parameters
- ord in python
# frag meth
- insert_class_data_item
- insert_class_static_values

View file

@ -151,17 +151,25 @@ impl DexFragment {
.map(|(idx, method)| (method.clone(), idx)) .map(|(idx, method)| (method.clone(), idx))
.collect::<HashMap<IdMethod, usize>>(); .collect::<HashMap<IdMethod, usize>>();
let index = FragIndex {
strings: strings_index,
types: types_index,
protos: protos_index,
fields: fields_index,
methods: methods_index,
};
frag.section_manager frag.section_manager
.add_elt(FragSection::ClassDefItem, None); .add_elt(FragSection::ClassDefItem, None);
frag.class_def.class_idx = *types_index.get(&class.descriptor).ok_or(anyhow!( frag.class_def.class_idx = *index.types.get(&class.descriptor).ok_or(anyhow!(
"Type {} (type of class {}) not found in type list", "Type {} (type of class {}) not found in type list",
class.descriptor.__str__(), class.descriptor.__str__(),
class.__str__() class.__str__()
))? as u32; ))? as u32;
frag.class_def.access_flags = class.get_raw_access_flags(); frag.class_def.access_flags = class.get_raw_access_flags();
frag.class_def.superclass_idx = if let Some(sup) = &class.superclass { frag.class_def.superclass_idx = if let Some(sup) = &class.superclass {
*types_index.get(sup).ok_or(anyhow!( *index.types.get(sup).ok_or(anyhow!(
"Type {} (superclass of class {}) not found in dex builder", "Type {} (superclass of class {}) not found in dex builder",
sup.__str__(), sup.__str__(),
class.__str__() class.__str__()
@ -171,7 +179,7 @@ impl DexFragment {
}; };
frag.class_def.interfaces_off = 0; // set when linking frag.class_def.interfaces_off = 0; // set when linking
frag.class_def.source_file_idx = if let Some(file) = &class.source_file { frag.class_def.source_file_idx = if let Some(file) = &class.source_file {
*strings_index.get(file).ok_or(anyhow!( *index.strings.get(file).ok_or(anyhow!(
"String {} (source file of class {}) not found in dex builder", "String {} (source file of class {}) not found in dex builder",
file.__str__(), file.__str__(),
class.__str__() class.__str__()
@ -180,7 +188,7 @@ impl DexFragment {
NO_INDEX.0 NO_INDEX.0
}; };
frag.class_def.annotations_off = if class.has_annotations() { frag.class_def.annotations_off = if class.has_annotations() {
frag.insert_annotations(class)?; frag.insert_annotations(class, &index)?;
1 1
} else { } else {
0 0
@ -214,12 +222,13 @@ impl DexFragment {
/// in the whole file or 0), their value is set to the actual value prelink value + 1. This allow /// 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 /// to distinguish the offset of the first item (equal to zero before linking) and the value
/// 0 used to indicate an abscence of item. /// 0 used to indicate an abscence of item.
fn insert_annotations(&mut self, class: &Class) -> Result<()> { fn insert_annotations(&mut self, class: &Class, index: &FragIndex) -> Result<()> {
let class_annotations_off = if !class.annotations.is_empty() { let class_annotations_off = if !class.annotations.is_empty() {
let class_annotations_off = self let class_annotations_off = self
.section_manager .section_manager
.get_aligned_size(FragSection::AnnotationSetItem); .get_aligned_size(FragSection::AnnotationSetItem);
self.insert_class_annotation_set(class).with_context(|| { self.insert_class_annotation_set(class, index)
.with_context(|| {
format!( format!(
"Failed to insert class annotation for class {}", "Failed to insert class annotation for class {}",
class.__str__() class.__str__()
@ -231,13 +240,11 @@ impl DexFragment {
}; };
let mut field_ids = vec![]; 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.static_fields.keys().cloned());
field_ids.extend(class.instance_fields.keys().cloned()); field_ids.extend(class.instance_fields.keys().cloned());
field_ids.sort(); field_ids.sort();
let mut field_annotations = vec![]; let mut field_annotations = vec![];
for field_id in field_ids { 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 static_field = class.static_fields.get(&field_id);
let instance_field = class.instance_fields.get(&field_id); let instance_field = class.instance_fields.get(&field_id);
let (is_static, field) = match (static_field, instance_field) { let (is_static, field) = match (static_field, instance_field) {
@ -245,33 +252,31 @@ impl DexFragment {
(None, Some(field)) => (false, field), (None, Some(field)) => (false, field),
_ => bail!( _ => bail!(
"Unexpected configuration: field {} is both a static and a instance field in {}", "Unexpected configuration: field {} is both a static and a instance field in {}",
field_id.__str__(), class_id.__str__()), field_id.__str__(), class.__str__()),
}; };
if !field.annotations.is_empty() { if !field.annotations.is_empty() {
let annotations_off = self let annotations_off = self
.section_manager .section_manager
.get_aligned_size(Section::AnnotationSetItem) .get_aligned_size(FragSection::AnnotationSetItem)
+ 1; + 1;
self.insert_field_annotation_set(&field_id, is_static)?; self.insert_field_annotation_set(field, index)?;
field_annotations.push(FieldAnnotation { field_annotations.push(FieldAnnotation {
field_idx: *self.field_ids.get(&field_id).ok_or(anyhow!( field_idx: *index.fields.get(&field_id).ok_or(anyhow!(
"Field {} in {} not found in dex builder", "Field {} in {} not found in dex builder",
field_id.__str__(), field_id.__str__(),
class_id.__str__(), class.__str__(),
))? as u32, ))? as u32,
annotations_off, // linked in link_annotations() annotations_off,
}); });
} }
} }
let mut method_ids = vec![]; 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.direct_methods.keys().cloned());
method_ids.extend(class.virtual_methods.keys().cloned()); method_ids.extend(class.virtual_methods.keys().cloned());
method_ids.sort(); method_ids.sort();
let mut method_annotations = vec![]; let mut method_annotations = vec![];
for method_id in &method_ids { 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 direct_method = class.direct_methods.get(method_id);
let virtual_method = class.virtual_methods.get(method_id); let virtual_method = class.virtual_methods.get(method_id);
let (is_direct, method) = match (direct_method, virtual_method) { let (is_direct, method) = match (direct_method, virtual_method) {
@ -279,28 +284,27 @@ impl DexFragment {
(None, Some(method)) => (false, method), (None, Some(method)) => (false, method),
_ => bail!( _ => bail!(
"Unexpected configuration: method {} is both a direct and a virtual method in {}", "Unexpected configuration: method {} is both a direct and a virtual method in {}",
method_id.__str__(), class_id.__str__()), method_id.__str__(), class.__str__()),
}; };
if !method.annotations.is_empty() { if !method.annotations.is_empty() {
let annotations_off = self let annotations_off = self
.section_manager .section_manager
.get_aligned_size(Section::AnnotationSetItem) .get_aligned_size(FragSection::AnnotationSetItem)
+ 1; + 1;
self.insert_method_annotation_set(method_id, is_direct)?; self.insert_method_annotation_set(method, index)?;
method_annotations.push(MethodAnnotation { method_annotations.push(MethodAnnotation {
method_idx: *self.method_ids.get(method_id).ok_or(anyhow!( method_idx: *index.methods.get(method_id).ok_or(anyhow!(
"Method {} in {} not found in dex builder", "Method {} in {} not found in dex builder",
method_id.__str__(), method_id.__str__(),
class_id.__str__(), class.__str__(),
))? as u32, ))? as u32,
annotations_off, // linked in link_annotations() annotations_off,
}); });
} }
} }
let mut parameter_annotations = vec![]; let mut parameter_annotations = vec![];
for method_id in method_ids { 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 direct_method = class.direct_methods.get(&method_id);
let virtual_method = class.virtual_methods.get(&method_id); let virtual_method = class.virtual_methods.get(&method_id);
let (is_direct, method) = match (direct_method, virtual_method) { let (is_direct, method) = match (direct_method, virtual_method) {
@ -308,39 +312,39 @@ impl DexFragment {
(None, Some(method)) => (false, method), (None, Some(method)) => (false, method),
_ => bail!( _ => bail!(
"Unexpected configuration: method {} is both a direct and a virtual method in {}", "Unexpected configuration: method {} is both a direct and a virtual method in {}",
method_id.__str__(), class_id.__str__()), method_id.__str__(), class.__str__()),
}; };
if !method.parameters_annotations.is_empty() { if !method.parameters_annotations.is_empty() {
let annotations_off = self let annotations_off = self
.section_manager .section_manager
.get_aligned_size(Section::AnnotationSetRefList) .get_aligned_size(FragSection::AnnotationSetRefList)
+ 1; + 1;
self.insert_parameters_annotation_set_list(&method_id, is_direct)?; self.insert_parameters_annotation_set_list(method, index)?;
parameter_annotations.push(ParameterAnnotation { parameter_annotations.push(ParameterAnnotation {
method_idx: *self.method_ids.get(&method_id).ok_or(anyhow!( method_idx: *index.methods.get(&method_id).ok_or(anyhow!(
"Method {} in {} not found in dex builder", "Method {} in {} not found in dex builder",
method_id.__str__(), method_id.__str__(),
class_id.__str__(), class.__str__(),
))? as u32, ))? as u32,
annotations_off, // linked in link_annotations() annotations_off,
}); });
} }
} }
let item = AnnotationsDirectoryItem { let item = AnnotationsDirectoryItem {
class_annotations_off, // linked in link_annotations() class_annotations_off,
field_annotations, field_annotations,
method_annotations, method_annotations,
parameter_annotations, parameter_annotations,
}; };
self.section_manager self.section_manager
.add_elt(FragSection::AnnotationsDirectoryItem, Some(item.size())); .add_elt(FragSection::AnnotationsDirectoryItem, Some(item.size()));
self.annotations_directory_items.push(item); self.annotations_directory_items = Some(item);
Ok(()) Ok(())
} }
/// Insert the annnotations set for a class. /// Insert the annnotations set for a class.
fn insert_class_annotation_set(&mut self, class: &Class) -> Result<()> { fn insert_class_annotation_set(&mut self, class: &Class, index: &FragIndex) -> Result<()> {
let mut annotations = class.annotations.clone(); let mut annotations = class.annotations.clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] }; let mut annotation_set = AnnotationSetItem { entries: vec![] };
@ -367,7 +371,7 @@ impl DexFragment {
runtime or system" runtime or system"
), // TODO: check if this is true ), // TODO: check if this is true
}, },
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?,
}; };
self.section_manager self.section_manager
.add_elt(FragSection::AnnotationItem, Some(item.size())); .add_elt(FragSection::AnnotationItem, Some(item.size()));
@ -379,6 +383,170 @@ impl DexFragment {
Ok(()) Ok(())
} }
/// Insert the annnotations set for a class.
fn insert_field_annotation_set(&mut self, field: &Field, index: &FragIndex) -> Result<()> {
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(FragSection::AnnotationItem),
});
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, index)?,
};
self.section_manager
.add_elt(FragSection::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(FragSection::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: &Method, index: &FragIndex) -> Result<()> {
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(FragSection::AnnotationItem)
+ 1,
});
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, index)?,
};
self.section_manager
.add_elt(FragSection::AnnotationItem, Some(item.size()));
self.annotation_items.push(item);
}
self.section_manager
.add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set);
Ok(())
}
/// Insert a [`MethodHandle`].
pub fn insert_method_handle(&mut self, handle: &MethodHandle, index: &FragIndex) -> Result<()> {
let (field_or_method_id, method_handle_type) = match handle {
MethodHandle::StaticPut(StaticPut(field)) => (
*index
.fields
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::StaticPut,
),
MethodHandle::StaticGet(StaticGet(field)) => (
*index
.fields
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::StaticGet,
),
MethodHandle::InstancePut(InstancePut(field)) => (
*index
.fields
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::InstancePut,
),
MethodHandle::InstanceGet(InstanceGet(field)) => (
*index
.fields
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::InstanceGet,
),
MethodHandle::InvokeStatic(InvokeStatic(meth)) => (
*index
.methods
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeStatic,
),
MethodHandle::InvokeInstance(InvokeInstance(meth)) => (
*index
.methods
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeInstance,
),
MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => (
*index
.methods
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeConstructor,
),
MethodHandle::InvokeDirect(InvokeDirect(meth)) => (
*index
.methods
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeDirect,
),
MethodHandle::InvokeInterface(InvokeInterface(meth)) => (
*index
.methods
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeInterface,
),
};
self.method_handles.push(MethodHandleItem {
method_handle_type,
field_or_method_id,
unused1: 0,
unused2: 0,
});
Ok(())
}
/// Insert a code_item. /// Insert a code_item.
/// ///
/// # Warning /// # Warning
@ -1283,7 +1451,7 @@ impl DexFragment {
} }
Instruction::ConstMethodHandle(ins) => { Instruction::ConstMethodHandle(ins) => {
let method_handle_idx = self.method_handles.len(); let method_handle_idx = self.method_handles.len();
self.insert_method_handle(&ins.handle)?; self.insert_method_handle(&ins.handle, index)?;
let ins = ins.get_raw_ins(method_handle_idx); let ins = ins.get_raw_ins(method_handle_idx);
addr += ins.size() / 2; addr += ins.size() / 2;
insns.push(ins); insns.push(ins);
@ -1558,97 +1726,16 @@ impl DexFragment {
Ok(()) 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.__str__()))?
as u16,
MethodHandleType::StaticPut,
),
MethodHandle::StaticGet(StaticGet(field)) => (
*self
.field_ids
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::StaticGet,
),
MethodHandle::InstancePut(InstancePut(field)) => (
*self
.field_ids
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::InstancePut,
),
MethodHandle::InstanceGet(InstanceGet(field)) => (
*self
.field_ids
.get(field)
.ok_or(anyhow!("Field {} not found in dex writer", field.__str__()))?
as u16,
MethodHandleType::InstanceGet,
),
MethodHandle::InvokeStatic(InvokeStatic(meth)) => (
*self
.method_ids
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeStatic,
),
MethodHandle::InvokeInstance(InvokeInstance(meth)) => (
*self
.method_ids
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeInstance,
),
MethodHandle::InvokeConstructor(InvokeConstructor(meth)) => (
*self
.method_ids
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeConstructor,
),
MethodHandle::InvokeDirect(InvokeDirect(meth)) => (
*self
.method_ids
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
as u16,
MethodHandleType::InvokeDirect,
),
MethodHandle::InvokeInterface(InvokeInterface(meth)) => (
*self
.method_ids
.get(meth)
.ok_or(anyhow!("Method {} not found in dex writer", meth.__str__()))?
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`]. /// Convert a [`DexValue`] to an [`EncodedValue`].
/// ///
/// # Warning /// # Warning
/// ///
/// This method can insert element in the dex file like method_handles. /// This method can insert element in the dex file like method_handles.
pub fn dex_value_to_encoded_value(&mut self, value: &DexValue) -> Result<EncodedValue> { pub fn dex_value_to_encoded_value(
&mut self,
value: &DexValue,
index: &FragIndex,
) -> Result<EncodedValue> {
match value { match value {
DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)), DexValue::Byte(DexByte(val)) => Ok(EncodedValue::Byte(*val)),
DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)), DexValue::Short(DexShort(val)) => Ok(EncodedValue::Short(*val)),
@ -1658,48 +1745,47 @@ impl DexFragment {
DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)), DexValue::Float(DexFloat(val)) => Ok(EncodedValue::Float(*val)),
DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)), DexValue::Double(DexDouble(val)) => Ok(EncodedValue::Double(*val)),
DexValue::MethodType(val) => Ok(EncodedValue::MethodType( DexValue::MethodType(val) => Ok(EncodedValue::MethodType(
*self.proto_ids.get(val).ok_or(anyhow!( *index.protos.get(val).ok_or(anyhow!(
"Prototype {} not found in dex writer", "Prototype {} not found in dex writer",
val.__str__() val.__str__()
))? as u32, ))? as u32,
)), )),
DexValue::MethodHandle(val) => { DexValue::MethodHandle(val) => {
// TODO: move to a method
let idx = self.method_handles.len() as u32; let idx = self.method_handles.len() as u32;
self.insert_method_handle(val)?; self.insert_method_handle(val, index)?;
Ok(EncodedValue::MethodHandle(idx)) Ok(EncodedValue::MethodHandle(idx))
} }
DexValue::String(val) => Ok(EncodedValue::String( DexValue::String(val) => Ok(EncodedValue::String(
*self *index
.strings .strings
.get(val) .get(val)
.ok_or(anyhow!("String {} not found in dex writer", val.__str__()))? .ok_or(anyhow!("String {} not found in dex writer", val.__str__()))?
as u32, as u32,
)), )),
DexValue::Type(val) => Ok(EncodedValue::Type( DexValue::Type(val) => Ok(EncodedValue::Type(
*self *index
.type_ids .types
.get(val) .get(val)
.ok_or(anyhow!("Type {} not found in dex writer", val.__str__()))? .ok_or(anyhow!("Type {} not found in dex writer", val.__str__()))?
as u32, as u32,
)), )),
DexValue::Field(val) => Ok(EncodedValue::Field( DexValue::Field(val) => Ok(EncodedValue::Field(
*self *index
.field_ids .fields
.get(val) .get(val)
.ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))?
as u32, as u32,
)), )),
DexValue::Method(val) => Ok(EncodedValue::Method( DexValue::Method(val) => Ok(EncodedValue::Method(
*self *index
.method_ids .methods
.get(val) .get(val)
.ok_or(anyhow!("Method {} not found in dex writer", val.__str__()))? .ok_or(anyhow!("Method {} not found in dex writer", val.__str__()))?
as u32, as u32,
)), )),
DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum( DexValue::Enum(IdEnum(val)) => Ok(EncodedValue::Enum(
*self *index
.field_ids .fields
.get(val) .get(val)
.ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))? .ok_or(anyhow!("Field {} not found in dex writer", val.__str__()))?
as u32, as u32,
@ -1708,14 +1794,14 @@ impl DexFragment {
let mut values = vec![]; let mut values = vec![];
for val in arr { for val in arr {
values.push( values.push(
self.dex_value_to_encoded_value(val) self.dex_value_to_encoded_value(val, index)
.context("Error while serializing a array")?, .context("Error while serializing a array")?,
); );
} }
Ok(EncodedValue::Array(EncodedArray { values })) Ok(EncodedValue::Array(EncodedArray { values }))
} }
DexValue::Annotation(val) => Ok(EncodedValue::Annotation( DexValue::Annotation(val) => Ok(EncodedValue::Annotation(
self.dex_annotation_to_encoded_annotation(val.clone())?, self.dex_annotation_to_encoded_annotation(val.clone(), index)?,
)), )),
DexValue::Null(DexNull) => Ok(EncodedValue::Null), DexValue::Null(DexNull) => Ok(EncodedValue::Null),
DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)), DexValue::Boolean(DexBoolean(val)) => Ok(EncodedValue::Boolean(*val)),
@ -1723,16 +1809,20 @@ impl DexFragment {
} }
/// Insert an encoded_array in the encoded_array_item section. /// Insert an encoded_array in the encoded_array_item section.
fn insert_encoded_array_item(&mut self, DexArray(array): DexArray) -> Result<()> { fn insert_encoded_array_item(
&mut self,
DexArray(array): DexArray,
index: &FragIndex,
) -> Result<()> {
let mut values = vec![]; let mut values = vec![];
for value in array { for value in array {
values.push(self.dex_value_to_encoded_value(&value)?); values.push(self.dex_value_to_encoded_value(&value, index)?);
} }
let item = EncodedArrayItem { let item = EncodedArrayItem {
value: EncodedArray { values }, value: EncodedArray { values },
}; };
self.section_manager self.section_manager
.add_elt(Section::EncodedArrayItem, Some(item.size())); .add_elt(FragSection::EncodedArrayItem, Some(item.size()));
self.encoded_array_items.push(item); self.encoded_array_items.push(item);
Ok(()) Ok(())
} }
@ -1794,6 +1884,7 @@ impl DexFragment {
fn dex_annotation_to_encoded_annotation( fn dex_annotation_to_encoded_annotation(
&mut self, &mut self,
DexAnnotation { type_, elements }: DexAnnotation, DexAnnotation { type_, elements }: DexAnnotation,
index: &FragIndex,
) -> Result<EncodedAnnotation> { ) -> Result<EncodedAnnotation> {
let mut encoded_elements = vec![]; let mut encoded_elements = vec![];
@ -1803,7 +1894,7 @@ impl DexFragment {
for name in elements_names { for name in elements_names {
let elt = elements.get(name).unwrap(); let elt = elements.get(name).unwrap();
encoded_elements.push(AnnotationElement { encoded_elements.push(AnnotationElement {
name_idx: Uleb128(*self.strings.get(name).ok_or(anyhow!( name_idx: Uleb128(*index.strings.get(name).ok_or(anyhow!(
"{} (annotation element name) not found in dex builder", "{} (annotation element name) not found in dex builder",
name.__str__() name.__str__()
))? as u32), ))? as u32),
@ -1811,7 +1902,7 @@ impl DexFragment {
}); });
} }
Ok(EncodedAnnotation { Ok(EncodedAnnotation {
type_idx: Uleb128(*self.type_ids.get(&type_).ok_or(anyhow!( type_idx: Uleb128(*index.types.get(&type_).ok_or(anyhow!(
"Annotation type {} not found in dex builder", "Annotation type {} not found in dex builder",
type_.__str__(), type_.__str__(),
))? as u32), ))? as u32),
@ -1819,117 +1910,13 @@ impl DexFragment {
}) })
} }
/// 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. /// Insert the annotations set for a method parameter.
fn insert_parameters_annotation_set( fn insert_parameters_annotation_set(
&mut self, &mut self,
method_id: &IdMethod, method: &Method,
is_direct_method: bool,
parameter_idx: usize, parameter_idx: usize,
index: &FragIndex,
) -> Result<()> { ) -> 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 annotations = method.parameters_annotations[parameter_idx].clone();
let mut annotation_set = AnnotationSetItem { entries: vec![] }; let mut annotation_set = AnnotationSetItem { entries: vec![] };
@ -1938,8 +1925,9 @@ impl DexFragment {
annotation_set.entries.push(AnnotationOffItem { annotation_set.entries.push(AnnotationOffItem {
annotation_off: self annotation_off: self
.section_manager .section_manager
.get_aligned_size(Section::AnnotationItem), .get_aligned_size(FragSection::AnnotationItem)
}); // linked in link_annotations() + 1,
});
let item = AnnotationItem { let item = AnnotationItem {
visibility: match ( visibility: match (
@ -1955,14 +1943,14 @@ impl DexFragment {
runtime or system" runtime or system"
), // TODO: check if this is true ), // TODO: check if this is true
}, },
annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation, index)?,
}; };
self.section_manager self.section_manager
.add_elt(Section::AnnotationItem, Some(item.size())); .add_elt(FragSection::AnnotationItem, Some(item.size()));
self.annotation_items.push(item); self.annotation_items.push(item);
} }
self.section_manager self.section_manager
.add_elt(Section::AnnotationSetItem, Some(annotation_set.size())); .add_elt(FragSection::AnnotationSetItem, Some(annotation_set.size()));
self.annotation_set_items.push(annotation_set); self.annotation_set_items.push(annotation_set);
Ok(()) Ok(())
} }
@ -1970,16 +1958,10 @@ impl DexFragment {
/// Insert the annotations set list for a method parameters. /// Insert the annotations set list for a method parameters.
fn insert_parameters_annotation_set_list( fn insert_parameters_annotation_set_list(
&mut self, &mut self,
method_id: &IdMethod, method: &Method,
is_direct_method: bool, index: &FragIndex,
) -> Result<()> { ) -> Result<()> {
let mut list = AnnotationSetRefList { list: vec![] }; 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 let param_has_annotation: Vec<_> = method
.parameters_annotations .parameters_annotations
.iter() .iter()
@ -1990,17 +1972,17 @@ impl DexFragment {
annotations_off: if has_annotation { annotations_off: if has_annotation {
let annotation_off = self let annotation_off = self
.section_manager .section_manager
.get_aligned_size(Section::AnnotationSetItem); .get_aligned_size(FragSection::AnnotationSetItem);
self.insert_parameters_annotation_set(method_id, is_direct_method, param_idx)?; self.insert_parameters_annotation_set(method, param_idx, index)?;
annotation_off + 1 annotation_off + 1
} else { } else {
0 0
}, // linked in link_annotations() },
}); });
} }
self.section_manager self.section_manager
.add_elt(Section::AnnotationSetRefList, Some(list.size())); .add_elt(FragSection::AnnotationSetRefList, Some(list.size()));
self.annotation_set_lists.push(list); self.annotation_set_lists.push(list);
Ok(()) Ok(())
} }
@ -2879,3 +2861,12 @@ impl FragSectionManager {
} }
} }
} }
/// Index that associate a type to its local id in a fragment.
struct FragIndex {
pub strings: HashMap<DexString, usize>,
pub types: HashMap<IdType, usize>,
pub protos: HashMap<IdMethodType, usize>,
pub fields: HashMap<IdField, usize>,
pub methods: HashMap<IdMethod, usize>,
}