seriable try block and call site

This commit is contained in:
Jean-Marie Mineau 2024-01-02 21:09:22 +01:00
parent 31e4192eb3
commit 04a6d48e51
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
3 changed files with 167 additions and 92 deletions

View file

@ -43,7 +43,7 @@ pub struct DexWriter {
/// index in the `class_defs` section. /// index in the `class_defs` section.
class_defs: HashMap<IdType, (Class, usize)>, class_defs: HashMap<IdType, (Class, usize)>,
/// The call sites refered to in the bytecode. /// The call sites refered to in the bytecode.
call_sites: Vec<CallSite>, call_site_ids: Vec<CallSiteIdItem>,
/// A struct that keep track of sections size, nb elements and offsets. /// A struct that keep track of sections size, nb elements and offsets.
section_manager: SectionManager, section_manager: SectionManager,
/// The string_data section. Once populated, the elements must be sorted according to the spec. /// The string_data section. Once populated, the elements must be sorted according to the spec.
@ -125,7 +125,7 @@ impl Default for DexWriter {
field_ids: HashMap::new(), field_ids: HashMap::new(),
method_ids: HashMap::new(), method_ids: HashMap::new(),
class_defs: HashMap::new(), class_defs: HashMap::new(),
call_sites: vec![], call_site_ids: vec![],
section_manager: SectionManager::default(), section_manager: SectionManager::default(),
string_data_list: vec![], string_data_list: vec![],
type_ids_list: vec![], type_ids_list: vec![],
@ -378,6 +378,7 @@ impl DexWriter {
.clone() .clone()
}; };
// Estimate instructions addresses
let mut min_addr = 0; let mut min_addr = 0;
let mut max_addr = 0; let mut max_addr = 0;
let mut label_min_max_addrs: HashMap<String, (usize, usize)> = HashMap::new(); let mut label_min_max_addrs: HashMap<String, (usize, usize)> = HashMap::new();
@ -404,6 +405,7 @@ impl DexWriter {
} }
} }
} }
// Compute instruction size and precise addresses
let mut addr = 0; let mut addr = 0;
let mut label_addrs = HashMap::new(); let mut label_addrs = HashMap::new();
let mut goto_sizes = vec![]; let mut goto_sizes = vec![];
@ -432,6 +434,10 @@ impl DexWriter {
_ => addr += ins.ins_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 insns = vec![];
let mut payloads = vec![]; let mut payloads = vec![];
let mut goto_idx = 0; let mut goto_idx = 0;
@ -1182,8 +1188,8 @@ impl DexWriter {
insns.push(ins); insns.push(ins);
} }
Instruction::InvokeCustom(ins) => { Instruction::InvokeCustom(ins) => {
let call_site_idx = self.call_sites.len(); let call_site_idx = self.call_site_ids.len();
self.call_sites.push(ins.call_site.clone()); self.insert_call_site_item(&ins.call_site)?;
let ins = ins.get_raw_ins(call_site_idx); let ins = ins.get_raw_ins(call_site_idx);
addr += ins.size() / 2; addr += ins.size() / 2;
insns.push(ins); insns.push(ins);
@ -1205,8 +1211,57 @@ impl DexWriter {
addr += ins.size() / 2; addr += ins.size() / 2;
insns.push(ins); insns.push(ins);
} }
Instruction::Try(_ins) => { Instruction::Try(try_) => {
// TODO 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,
};
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(_) => (), Instruction::Label(_) => (),
_ => { _ => {
@ -1224,10 +1279,6 @@ impl DexWriter {
} }
insns.extend(payloads); insns.extend(payloads);
// TODO
let mut tries = vec![];
let mut handlers = None;
let debug_info_off = if code.debug_info.is_empty() { let debug_info_off = if code.debug_info.is_empty() {
0 0
} else { } else {
@ -1238,6 +1289,11 @@ impl DexWriter {
self.debug_info_items.push(item); self.debug_info_items.push(item);
debug_info_off + 1 debug_info_off + 1
}; };
let handlers = if handlers.list.is_empty() {
None
} else {
Some(handlers)
};
let item = CodeItem { let item = CodeItem {
registers_size: code.registers_size, registers_size: code.registers_size,
ins_size: code.ins_size, ins_size: code.ins_size,
@ -1250,51 +1306,6 @@ impl DexWriter {
self.section_manager self.section_manager
.add_elt(Section::CodeItem, Some(item.size())); .add_elt(Section::CodeItem, Some(item.size()));
self.code_items.push(item); self.code_items.push(item);
/*
let mut tries = vec![];
let mut local_off = 0;
let mut handler_offsets = HashMap::new();
let mut handler_list = EncodedCatchHandlerList { list: vec![] };
for (start_addr, insn_count, (list, catch_all_addr)) in code.tries.iter().cloned() {
let mut handlers = vec![];
for (ty, addr) in list {
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(addr);
handlers.push(EncodedTypeAddrPair { type_idx, addr });
}
let handler = EncodedCatchHandler {
handlers,
catch_all_addr: catch_all_addr.map(Uleb128),
};
let handler_off = if let Some(handler_off) = handler_offsets.get(&handler).cloned() {
handler_off
} else {
let old_local_off = local_off;
local_off += handler.size() as u16;
handler_list.list.push(handler.clone());
handler_offsets.insert(handler, old_local_off);
old_local_off
};
tries.push(TryItem {
start_addr,
insn_count,
handler_off,
});
}
for try_ in tries.iter_mut() {
try_.handler_off += handler_list.size_field().size() as u16;
}
let handlers = if tries.is_empty() {
None
} else {
Some(handler_list)
};
*/
Ok(()) Ok(())
} }
@ -1597,6 +1608,24 @@ impl DexWriter {
Ok(()) 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_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. /// Insert the encoded_array_item encoding the static_values of a class.
fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> { fn insert_class_static_values(&mut self, class_id: &IdType) -> Result<()> {
let (class, _) = self.class_defs.get(class_id).unwrap(); let (class, _) = self.class_defs.get(class_id).unwrap();
@ -1672,11 +1701,18 @@ impl DexWriter {
}); // linked in link_annotations() }); // linked in link_annotations()
let item = AnnotationItem { let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) { visibility: match (
(true, false, false) => AnnotationVisibility::Build, annot.visibility_build,
(false, true, false) => AnnotationVisibility::Runtime, annot.visibility_runtime,
(false, false, true) => AnnotationVisibility::System, annot.visibility_system,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true ) {
(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)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
}; };
@ -1712,11 +1748,18 @@ impl DexWriter {
}); // linked in link_annotations() }); // linked in link_annotations()
let item = AnnotationItem { let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) { visibility: match (
(true, false, false) => AnnotationVisibility::Build, annot.visibility_build,
(false, true, false) => AnnotationVisibility::Runtime, annot.visibility_runtime,
(false, false, true) => AnnotationVisibility::System, annot.visibility_system,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true ) {
(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)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
}; };
@ -1752,11 +1795,18 @@ impl DexWriter {
}); // linked in link_annotations() }); // linked in link_annotations()
let item = AnnotationItem { let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) { visibility: match (
(true, false, false) => AnnotationVisibility::Build, annot.visibility_build,
(false, true, false) => AnnotationVisibility::Runtime, annot.visibility_runtime,
(false, false, true) => AnnotationVisibility::System, annot.visibility_system,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true ) {
(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)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
}; };
@ -1793,11 +1843,18 @@ impl DexWriter {
}); // linked in link_annotations() }); // linked in link_annotations()
let item = AnnotationItem { let item = AnnotationItem {
visibility: match (annot.visibility_build, annot.visibility_runtime, annot.visibility_system) { visibility: match (
(true, false, false) => AnnotationVisibility::Build, annot.visibility_build,
(false, true, false) => AnnotationVisibility::Runtime, annot.visibility_runtime,
(false, false, true) => AnnotationVisibility::System, annot.visibility_system,
_ => bail!("Annotation need visibility set to one and only one of build, runtime or system"), // TODO: check if this is true ) {
(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)?, annotation: self.dex_annotation_to_encoded_annotation(annot.annotation)?,
}; };
@ -2173,6 +2230,18 @@ impl DexWriter {
self.header.data_off = self.section_manager.get_offset(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 of type_lists items in proto_id_items and class_def_items. /// Link the offsets of type_lists items in proto_id_items and class_def_items.
/// ///
/// # Warning /// # Warning
@ -2338,7 +2407,9 @@ impl DexWriter {
self.get_map_list()?; self.get_map_list()?;
// From now on, all section are generated and the value in section_manager do not change.
self.link_header(); self.link_header();
self.link_call_site_ids();
self.link_type_list_occurences()?; self.link_type_list_occurences()?;
self.link_class_data_occurences(); self.link_class_data_occurences();
self.link_static_values(); self.link_static_values();
@ -2378,7 +2449,11 @@ impl DexWriter {
for class_def in &self.class_defs_list { for class_def in &self.class_defs_list {
class_def.serialize(writer)?; class_def.serialize(writer)?;
} }
// TODO: CallSiteIdItem, data must be inserted as encoded array item later // CallSiteIdItem, data are inserted as encoded array item later
for call_site_id in &self.call_site_ids {
call_site_id.serialize(writer)?;
}
// MethodHandleItem section // MethodHandleItem section
for handle in &self.method_handles { for handle in &self.method_handles {
handle.serialize(writer)?; handle.serialize(writer)?;
@ -2428,7 +2503,7 @@ impl DexWriter {
for annot in &self.annotation_items { for annot in &self.annotation_items {
annot.serialize(writer)?; annot.serialize(writer)?;
} }
// TODO: EncodedArrayItem: partialy done // EncodedArrayItem section
for array in &self.encoded_array_items { for array in &self.encoded_array_items {
array.serialize(writer)?; array.serialize(writer)?;
} }

View file

@ -6027,7 +6027,7 @@ impl IfLeZ {
} }
} }
/// Put the value at arr[idx] in register dest (all values are in registers) /// Put the value at `arr[idx]` in register dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGet { pub struct AGet {
@ -6099,7 +6099,7 @@ impl AGet {
} }
} }
/// Put the value at arr[idx] in register pair dest (all values are in registers) /// Put the value at `arr[idx]` in register pair dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetWide { pub struct AGetWide {
@ -6171,7 +6171,7 @@ impl AGetWide {
} }
} }
/// Put the reference at arr[idx] in register (all values are in registers) /// Put the reference at `arr[idx]` in register (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetObject { pub struct AGetObject {
@ -6243,7 +6243,7 @@ impl AGetObject {
} }
} }
/// Put the boolean at arr[idx] in register dest (all values are in registers) /// Put the boolean at `arr[idx]` in register dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetBoolean { pub struct AGetBoolean {
@ -6315,7 +6315,7 @@ impl AGetBoolean {
} }
} }
/// Put the byte at arr[idx] in register dest (all values are in registers) /// Put the byte at `arr[idx]` in register dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetByte { pub struct AGetByte {
@ -6387,7 +6387,7 @@ impl AGetByte {
} }
} }
/// Put the char at arr[idx] in register dest (all values are in registers) /// Put the char at `arr[idx]` in register dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetChar { pub struct AGetChar {
@ -6459,7 +6459,7 @@ impl AGetChar {
} }
} }
/// Put the short at arr[idx] in register dest (all values are in registers) /// Put the short at `arr[idx]` in register dest (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AGetShort { pub struct AGetShort {
@ -6531,7 +6531,7 @@ impl AGetShort {
} }
} }
/// Put the value of register 'from' in arr[idx] (all values are in registers) /// Put the value of register 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APut { pub struct APut {
@ -6603,7 +6603,7 @@ impl APut {
} }
} }
/// Put the value of the register pair 'from' in arr[idx] (all values are in registers) /// Put the value of the register pair 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutWide { pub struct APutWide {
@ -6675,7 +6675,7 @@ impl APutWide {
} }
} }
/// Put the object reference in 'from' in arr[idx] (all values are in registers) /// Put the object reference in 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutObject { pub struct APutObject {
@ -6747,7 +6747,7 @@ impl APutObject {
} }
} }
/// Put the boolean in 'from' in arr[idx] (all values are in registers) /// Put the boolean in 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutBoolean { pub struct APutBoolean {
@ -6819,7 +6819,7 @@ impl APutBoolean {
} }
} }
/// Put the byte in 'from' in arr[idx] (all values are in registers) /// Put the byte in 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutByte { pub struct APutByte {
@ -6891,7 +6891,7 @@ impl APutByte {
} }
} }
/// Put the char in 'from' in arr[idx] (all values are in registers) /// Put the char in 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutChar { pub struct APutChar {
@ -6963,7 +6963,7 @@ impl APutChar {
} }
} }
/// Put the short in 'from' in arr[idx] (all values are in registers) /// Put the short in 'from' in `arr[idx]` (all values are in registers)
#[pyclass] #[pyclass]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct APutShort { pub struct APutShort {

View file

@ -21,7 +21,7 @@ pub struct ClassDefItem {
pub interfaces_off: u32, pub interfaces_off: u32,
/// Index of a [`crate::StringIdItem`] in `string_ids` or [`crate::NO_INDEX`]. /// Index of a [`crate::StringIdItem`] in `string_ids` or [`crate::NO_INDEX`].
pub source_file_idx: u32, pub source_file_idx: u32,
/// 0 if no annotation, else offset to a [`crate::AnnotationDirectoryItem`]. /// 0 if no annotation, else offset to a [`crate::AnnotationsDirectoryItem`].
pub annotations_off: u32, pub annotations_off: u32,
/// 0 if no data for this class, else offset to a [`crate::ClassDataItem`]. /// 0 if no data for this class, else offset to a [`crate::ClassDataItem`].
pub class_data_off: u32, pub class_data_off: u32,