1use std::{
2 backtrace::Backtrace,
3 collections::{BTreeMap, BTreeSet},
4 fmt::{Display, Formatter},
5};
6
7use snafu::Snafu;
8use unarm::{
9 ArmVersion, Endian, Ins, ParseFlags, ParseMode, ParsedIns, Parser,
10 args::{Argument, Reg, Register},
11 arm, thumb,
12};
13
14use super::{
15 function_branch::FunctionBranchState,
16 function_start::is_valid_function_start,
17 illegal_code::IllegalCodeState,
18 inline_table::{InlineTable, InlineTableState},
19 jump_table::{JumpTable, JumpTableState},
20 secure_area::SecureAreaState,
21};
22use crate::{
23 config::symbol::{SymbolMap, SymbolMapError},
24 util::bytes::FromSlice,
25};
26
27pub type Labels = BTreeSet<u32>;
29pub type PoolConstants = BTreeSet<u32>;
30pub type JumpTables = BTreeMap<u32, JumpTable>;
31pub type InlineTables = BTreeMap<u32, InlineTable>;
32pub type FunctionCalls = BTreeMap<u32, CalledFunction>;
33pub type DataLoads = BTreeMap<u32, u32>;
34
35#[derive(Debug, Clone)]
36pub struct Function {
37 name: String,
38 start_address: u32,
39 end_address: u32,
40 first_instruction_address: u32,
41 thumb: bool,
42 labels: Labels,
43 pool_constants: PoolConstants,
44 jump_tables: JumpTables,
45 inline_tables: InlineTables,
46 function_calls: FunctionCalls,
47}
48
49#[derive(Debug, Snafu)]
50pub enum FunctionAnalysisError {
51 #[snafu(transparent)]
52 IntoFunction { source: IntoFunctionError },
53 #[snafu(transparent)]
54 SymbolMap { source: SymbolMapError },
55}
56
57const PARSE_FLAGS: ParseFlags = ParseFlags { version: ArmVersion::V5Te, ual: false };
58
59impl Function {
60 pub fn size(&self) -> u32 {
61 self.end_address - self.first_instruction_address
62 }
63
64 fn is_thumb_function(address: u32, code: &[u8]) -> bool {
65 if (address & 3) != 0 {
66 true
68 } else if code.len() < 4 {
69 true
71 } else if code[3] & 0xf0 == 0xe0 {
72 false
74 } else {
75 true
77 }
78 }
79
80 #[allow(clippy::match_like_matches_macro)]
81 fn is_entry_instruction(ins: Ins, parsed_ins: &ParsedIns) -> bool {
82 if ins.is_conditional() {
83 return false;
84 }
85
86 let args = &parsed_ins.args;
87 match (parsed_ins.mnemonic, args[0], args[1], args[2]) {
88 (
89 "stmdb",
90 Argument::Reg(Reg { reg: Register::Sp, writeback: true, deref: false }),
91 Argument::RegList(regs),
92 Argument::None,
93 )
94 | ("push", Argument::RegList(regs), Argument::None, Argument::None)
95 if regs.contains(Register::Lr) =>
96 {
97 true
98 }
99 _ => false,
100 }
101 }
102
103 fn is_branch(ins: Ins, parsed_ins: &ParsedIns, address: u32) -> Option<u32> {
104 if ins.mnemonic() != "b" {
105 return None;
106 }
107 let dest = parsed_ins.branch_destination().unwrap();
108 Some((address as i32 + dest).try_into().unwrap())
109 }
110
111 fn is_pool_load(ins: Ins, parsed_ins: &ParsedIns, address: u32, thumb: bool) -> Option<u32> {
112 if ins.mnemonic() != "ldr" {
113 return None;
114 }
115 match (parsed_ins.args[0], parsed_ins.args[1], parsed_ins.args[2]) {
116 (Argument::Reg(dest), Argument::Reg(base), Argument::OffsetImm(offset)) => {
117 if dest.reg == Register::Pc {
118 None
119 } else if !base.deref || base.reg != Register::Pc {
120 None
121 } else if offset.post_indexed {
122 None
123 } else {
124 let load_address = (address as i32 + offset.value) as u32 & !3;
126 let load_address = load_address + if thumb { 4 } else { 8 };
127 Some(load_address)
128 }
129 }
130 _ => None,
131 }
132 }
133
134 fn is_function_call(
135 ins: Ins,
136 parsed_ins: &ParsedIns,
137 address: u32,
138 thumb: bool,
139 ) -> Option<CalledFunction> {
140 let args = &parsed_ins.args;
141 match (ins.mnemonic(), args[0], args[1]) {
142 ("bl", Argument::BranchDest(offset), Argument::None) => {
143 let destination = (address as i32 + offset) as u32;
144 Some(CalledFunction { ins, address: destination, thumb })
145 }
146 ("blx", Argument::BranchDest(offset), Argument::None) => {
147 let destination = (address as i32 + offset) as u32;
148 let destination = if thumb { destination & !3 } else { destination };
149 Some(CalledFunction { ins, address: destination, thumb: !thumb })
150 }
151 _ => None,
152 }
153 }
154
155 fn function_parser_loop(
156 mut parser: Parser<'_>,
157 options: FunctionParseOptions,
158 ) -> Result<Function, FunctionAnalysisError> {
159 let thumb = parser.mode == ParseMode::Thumb;
160 let mut context = ParseFunctionContext::new(thumb, options);
161
162 let Some((address, ins, parsed_ins)) = parser.next() else {
163 return Err(FunctionAnalysisError::IntoFunction {
164 source: NoEpilogueSnafu.build().into(),
165 });
166 };
167 if !is_valid_function_start(address, ins, &parsed_ins) {
168 return Err(FunctionAnalysisError::IntoFunction {
169 source: InvalidStartSnafu { address, ins }.build().into(),
170 });
171 }
172
173 let state = context.handle_ins(&mut parser, address, ins, parsed_ins);
174 let mut function = if state.ended() {
175 return Ok(context.into_function(state)?);
176 } else {
177 loop {
178 let Some((address, ins, parsed_ins)) = parser.next() else {
179 break context.into_function(ParseFunctionState::Done)?;
180 };
181 let state = context.handle_ins(&mut parser, address, ins, parsed_ins);
182 if state.ended() {
183 break context.into_function(state)?;
184 }
185 }
186 };
187
188 if let Some(first_pool_address) = function.pool_constants.first()
189 && *first_pool_address < function.start_address
190 {
191 log::info!(
192 "Function at {:#010x} was adjusted to include pre-code constant pool at {:#010x}",
193 function.start_address,
194 first_pool_address
195 );
196
197 function.first_instruction_address = function.start_address;
198 function.start_address = *first_pool_address;
199 }
200
201 Ok(function)
202 }
203
204 pub fn parse_function(
205 options: FunctionParseOptions,
206 ) -> Result<Function, FunctionAnalysisError> {
207 let FunctionParseOptions {
208 start_address, base_address, module_code, parse_options, ..
209 } = &options;
210
211 let thumb =
212 parse_options.thumb.unwrap_or(Function::is_thumb_function(*start_address, module_code));
213 let parse_mode = if thumb { ParseMode::Thumb } else { ParseMode::Arm };
214 let start = (start_address - base_address) as usize;
215 let function_code = &module_code[start..];
216 let parser =
217 Parser::new(parse_mode, *start_address, Endian::Little, PARSE_FLAGS, function_code);
218
219 Self::function_parser_loop(parser, options)
220 }
221
222 pub fn find_functions(
223 options: FindFunctionsOptions,
224 ) -> Result<BTreeMap<u32, Function>, FunctionAnalysisError> {
225 let FindFunctionsOptions {
226 default_name_prefix,
227 base_address,
228 module_code,
229 symbol_map,
230 module_start_address,
231 module_end_address,
232 search_options,
233 } = options;
234
235 let mut functions = BTreeMap::new();
236
237 let start_address = search_options.start_address.unwrap_or(base_address);
238 assert!((start_address & 1) == 0);
239 let start_offset = start_address - base_address;
240 let end_address =
241 search_options.end_address.unwrap_or(base_address + module_code.len() as u32);
242 let end_offset = end_address - base_address;
243 let module_code = &module_code[..end_offset as usize];
244 let mut function_code = &module_code[start_offset as usize..end_offset as usize];
245
246 log::debug!(
247 "Searching for functions from {:#010x} to {:#010x}",
248 start_address,
249 end_address
250 );
251
252 let last_function_address = search_options.last_function_address.unwrap_or(end_address);
254 let mut upper_bounds = BTreeSet::new();
255
256 let mut prev_valid_address = start_address;
258 let mut address = start_address;
259
260 while !function_code.is_empty()
261 && address <= *upper_bounds.first().unwrap_or(&last_function_address)
262 {
263 let thumb = Function::is_thumb_function(address, function_code);
264
265 let parse_mode = if thumb { ParseMode::Thumb } else { ParseMode::Arm };
266 let parser =
267 Parser::new(parse_mode, address, Endian::Little, PARSE_FLAGS, function_code);
268
269 let (name, new) = if let Some((_, symbol)) = symbol_map.by_address(address)? {
270 (symbol.name.clone(), false)
271 } else {
272 (format!("{default_name_prefix}{address:08x}"), true)
273 };
274
275 let function_result = Function::function_parser_loop(parser, FunctionParseOptions {
276 name,
277 start_address: address,
278 base_address,
279 module_code,
280 known_end_address: None,
281 module_start_address,
282 module_end_address,
283 existing_functions: search_options.existing_functions,
284 check_defs_uses: search_options.check_defs_uses,
285 parse_options: Default::default(),
286 });
287 let function = match function_result {
288 Ok(function) => function,
289 Err(FunctionAnalysisError::IntoFunction {
290 source: IntoFunctionError::ParseFunction { source },
291 }) => {
292 match source {
293 ParseFunctionError::IllegalIns {
294 address: illegal_address, ins, ..
295 } => {
296 let search_limit = prev_valid_address
297 .saturating_add(search_options.max_function_start_search_distance);
298 let limit_reached = address >= search_limit;
299
300 if !limit_reached {
301 let mut next_address = (address + 1).next_multiple_of(4);
304 if let Some(function_addresses) =
305 search_options.function_addresses.as_ref()
306 && let Some(&next_function) =
307 function_addresses.range(address + 1..).next()
308 {
309 next_address = next_function;
310 }
311 address = next_address;
312 function_code = &module_code[(address - base_address) as usize..];
313 continue;
314 } else {
315 if thumb {
316 log::debug!(
317 "Terminating function analysis due to illegal instruction at {:#010x}: {:04x}",
318 illegal_address,
319 ins.code()
320 );
321 } else {
322 log::debug!(
323 "Terminating function analysis due to illegal instruction at {:#010x}: {:08x}",
324 illegal_address,
325 ins.code()
326 );
327 }
328 break;
329 }
330 }
331 ParseFunctionError::NoEpilogue => {
332 log::debug!(
333 "Terminating function analysis due to no epilogue in function starting from {:#010x}",
334 address
335 );
336 break;
337 }
338 ParseFunctionError::InvalidStart { address: start_address, ins } => {
339 let search_limit = prev_valid_address
340 .saturating_add(search_options.max_function_start_search_distance);
341 let limit_reached = address >= search_limit;
342
343 if !limit_reached {
344 let ins_size = parse_mode.instruction_size(0);
345 address += ins_size as u32;
346 function_code = &function_code[ins_size..];
347 continue;
348 } else {
349 log::debug!(
350 "Terminating function analysis due to invalid function start at {:#010x}: {}",
351 start_address,
352 ins
353 );
354 break;
355 }
356 }
357 }
358 }
359 Err(e) => return Err(e),
360 };
361
362 if new {
364 symbol_map.add_function(&function);
365 }
366 function.add_local_symbols_to_map(symbol_map)?;
367
368 address = function.end_address.next_multiple_of(4); prev_valid_address = function.end_address;
370 function_code = &module_code[(address - base_address) as usize..];
371
372 let invalid_upper_bounds: Vec<u32> =
374 upper_bounds.range(..=function.end_address).copied().collect();
375 for invalid_upper_bound in invalid_upper_bounds {
376 upper_bounds.remove(&invalid_upper_bound);
377 log::debug!(
378 "Invalidating upper bound {:#010x} inside function {:#010x}",
379 invalid_upper_bound,
380 function.start_address
381 );
382 }
383
384 if search_options.use_data_as_upper_bound {
386 for pool_constant in function.iter_pool_constants(module_code, base_address) {
387 let pointer_value = pool_constant.value & !1;
388 if upper_bounds.contains(&pointer_value) {
389 continue;
390 }
391 if pointer_value < address {
392 continue;
393 }
394
395 let offset = (pointer_value - base_address) as usize;
396 if offset >= module_code.len() {
397 continue;
398 }
399
400 let thumb = Function::is_thumb_function(pointer_value, &module_code[offset..]);
401 let mut parser = Parser::new(
402 if thumb { ParseMode::Thumb } else { ParseMode::Arm },
403 pointer_value,
404 Endian::Little,
405 PARSE_FLAGS,
406 &module_code[offset..],
407 );
408 let (address, ins, parsed_ins) = parser.next().unwrap();
409 if is_valid_function_start(address, ins, &parsed_ins) {
410 continue;
411 }
412
413 upper_bounds.insert(pointer_value);
415 log::debug!(
416 "Upper bound found: address to data at {:#010x} from pool constant at {:#010x} from function {}",
417 pool_constant.value,
418 pool_constant.address,
419 function.name
420 );
421 }
422 }
423
424 functions.insert(function.first_instruction_address, function);
425 }
426 Ok(functions)
427 }
428
429 pub fn add_local_symbols_to_map(
430 &self,
431 symbol_map: &mut SymbolMap,
432 ) -> Result<(), SymbolMapError> {
433 for address in self.labels.iter() {
434 symbol_map.add_label(*address, self.thumb)?;
435 }
436 for address in self.pool_constants.iter() {
437 symbol_map.add_pool_constant(*address)?;
438 }
439 for jump_table in self.jump_tables() {
440 symbol_map.add_jump_table(jump_table)?;
441 }
442 for inline_table in self.inline_tables().values() {
443 symbol_map.add_skip_data(None, inline_table.address, (*inline_table).into())?;
444 }
445 Ok(())
446 }
447
448 pub fn find_secure_area_functions(
449 module_code: &[u8],
450 base_addr: u32,
451 symbol_map: &mut SymbolMap,
452 ) -> BTreeMap<u32, Function> {
453 let mut functions = BTreeMap::new();
454
455 let mut address = base_addr;
456 let mut state = SecureAreaState::default();
457 for ins_code in module_code.chunks_exact(2) {
458 let ins_code = u16::from_le_slice(ins_code);
459 let ins = thumb::Ins::new(ins_code as u32, &PARSE_FLAGS);
460 let parsed_ins = ins.parse(&PARSE_FLAGS);
461
462 state = state.handle(address, &parsed_ins);
463 if let Some(function) = state.get_function() {
464 let function = Function {
465 name: function.name().to_string(),
466 start_address: function.start(),
467 end_address: function.end(),
468 first_instruction_address: function.start(),
469 thumb: true,
470 labels: Labels::new(),
471 pool_constants: PoolConstants::new(),
472 jump_tables: JumpTables::new(),
473 inline_tables: InlineTables::new(),
474 function_calls: FunctionCalls::new(),
475 };
476 symbol_map.add_function(&function);
477 functions.insert(function.first_instruction_address, function);
478 }
479
480 address += 2;
481 }
482
483 functions
484 }
485
486 pub fn parser<'a>(&'a self, module_code: &'a [u8], base_address: u32) -> Parser<'a> {
487 Parser::new(
488 if self.thumb { ParseMode::Thumb } else { ParseMode::Arm },
489 self.start_address,
490 Endian::Little,
491 PARSE_FLAGS,
492 self.code(module_code, base_address),
493 )
494 }
495
496 pub fn code<'a>(&self, module_code: &'a [u8], base_address: u32) -> &'a [u8] {
497 let start = (self.start_address - base_address) as usize;
498 let end = (self.end_address - base_address) as usize;
499 &module_code[start..end]
500 }
501
502 pub fn name(&self) -> &str {
503 &self.name
504 }
505
506 pub fn start_address(&self) -> u32 {
507 self.start_address
508 }
509
510 pub fn end_address(&self) -> u32 {
511 self.end_address
512 }
513
514 pub fn first_instruction_address(&self) -> u32 {
515 self.first_instruction_address
516 }
517
518 pub fn is_thumb(&self) -> bool {
519 self.thumb
520 }
521
522 pub fn labels(&self) -> impl Iterator<Item = &u32> {
523 self.labels.iter()
524 }
525
526 pub fn jump_tables(&self) -> impl Iterator<Item = &JumpTable> {
527 self.jump_tables.values()
528 }
529
530 pub fn inline_tables(&self) -> &InlineTables {
531 &self.inline_tables
532 }
533
534 pub fn get_inline_table_at(&self, address: u32) -> Option<&InlineTable> {
535 Self::inline_table_at(&self.inline_tables, address)
536 }
537
538 fn inline_table_at(inline_tables: &InlineTables, address: u32) -> Option<&InlineTable> {
539 inline_tables
540 .values()
541 .find(|table| address >= table.address && address < table.address + table.size)
542 }
543
544 pub fn pool_constants(&self) -> &PoolConstants {
545 &self.pool_constants
546 }
547
548 pub fn iter_pool_constants<'a>(
549 &'a self,
550 module_code: &'a [u8],
551 base_address: u32,
552 ) -> impl Iterator<Item = PoolConstant> + 'a {
553 self.pool_constants.iter().map(move |&address| {
554 let start = (address - base_address) as usize;
555 let bytes = &module_code[start..];
556 PoolConstant { address, value: u32::from_le_slice(bytes) }
557 })
558 }
559
560 pub fn function_calls(&self) -> &FunctionCalls {
561 &self.function_calls
562 }
563}
564
565#[derive(Default)]
566pub struct FunctionParseOptions<'a> {
567 pub name: String,
568 pub start_address: u32,
569 pub base_address: u32,
570 pub module_code: &'a [u8],
571 pub known_end_address: Option<u32>,
572 pub module_start_address: u32,
573 pub module_end_address: u32,
574 pub existing_functions: Option<&'a BTreeMap<u32, Function>>,
575
576 pub check_defs_uses: bool,
578
579 pub parse_options: ParseFunctionOptions,
580}
581
582pub struct FindFunctionsOptions<'a> {
583 pub default_name_prefix: &'a str,
584 pub base_address: u32,
585 pub module_code: &'a [u8],
586 pub symbol_map: &'a mut SymbolMap,
587 pub module_start_address: u32,
588 pub module_end_address: u32,
589
590 pub search_options: FunctionSearchOptions<'a>,
591}
592
593struct ParseFunctionContext<'a> {
594 name: String,
595 start_address: u32,
596 thumb: bool,
597 end_address: Option<u32>,
598 known_end_address: Option<u32>,
599 labels: Labels,
600 pool_constants: PoolConstants,
601 jump_tables: JumpTables,
602 inline_tables: InlineTables,
603 function_calls: FunctionCalls,
604
605 module_start_address: u32,
606 module_end_address: u32,
607 existing_functions: Option<&'a BTreeMap<u32, Function>>,
608
609 last_conditional_destination: Option<u32>,
611 last_pool_address: Option<u32>,
613 jump_table_state: JumpTableState,
615 function_branch_state: FunctionBranchState,
617 inline_table_state: InlineTableState,
619 illegal_code_state: IllegalCodeState,
621
622 check_defs_uses: bool,
624 defined_registers: BTreeSet<Register>,
625
626 prev_ins: Option<Ins>,
627 prev_parsed_ins: Option<ParsedIns>,
628 prev_address: Option<u32>,
629}
630
631#[derive(Debug, Snafu)]
632pub enum IntoFunctionError {
633 #[snafu(display("Cannot turn parse context into function before parsing is done"))]
634 NotDone { backtrace: Backtrace },
635 #[snafu(transparent)]
636 ParseFunction { source: ParseFunctionError },
637}
638
639impl<'a> ParseFunctionContext<'a> {
640 pub fn new(thumb: bool, options: FunctionParseOptions<'a>) -> Self {
641 let FunctionParseOptions {
642 name,
643 start_address,
644 known_end_address,
645 module_start_address,
646 module_end_address,
647 existing_functions,
648 check_defs_uses,
649 ..
650 } = options;
651
652 let mut defined_registers = BTreeSet::new();
653 defined_registers.insert(Register::R0);
655 defined_registers.insert(Register::R1);
656 defined_registers.insert(Register::R2);
657 defined_registers.insert(Register::R3);
658 defined_registers.insert(Register::Sp);
660 defined_registers.insert(Register::Lr);
661 defined_registers.insert(Register::Pc);
662 defined_registers.insert(Register::R12);
664
665 Self {
666 name,
667 start_address,
668 thumb,
669 end_address: None,
670 known_end_address,
671 labels: Labels::new(),
672 pool_constants: PoolConstants::new(),
673 jump_tables: JumpTables::new(),
674 inline_tables: InlineTables::new(),
675 function_calls: FunctionCalls::new(),
676
677 module_start_address,
678 module_end_address,
679 existing_functions,
680
681 last_conditional_destination: None,
682 last_pool_address: None,
683 jump_table_state: if thumb {
684 JumpTableState::Thumb(Default::default())
685 } else {
686 JumpTableState::Arm(Default::default())
687 },
688 function_branch_state: Default::default(),
689 inline_table_state: Default::default(),
690 illegal_code_state: Default::default(),
691
692 check_defs_uses,
693 defined_registers,
694
695 prev_ins: None,
696 prev_parsed_ins: None,
697 prev_address: None,
698 }
699 }
700
701 fn handle_ins_inner(
702 &mut self,
703 parser: &mut Parser,
704 address: u32,
705 ins: Ins,
706 parsed_ins: &ParsedIns,
707 ) -> ParseFunctionState {
708 if self.pool_constants.contains(&address) {
709 parser.seek_forward(address + 4);
710 return ParseFunctionState::Continue;
711 }
712 if let Some(inline_table) = Function::inline_table_at(&self.inline_tables, address) {
713 parser.seek_forward(inline_table.address + inline_table.size);
714 return ParseFunctionState::Continue;
715 }
716
717 self.jump_table_state =
718 self.jump_table_state.handle(address, ins, parsed_ins, &mut self.jump_tables);
719 self.last_conditional_destination =
720 self.last_conditional_destination.max(self.jump_table_state.table_end_address());
721 if let Some(label) = self.jump_table_state.get_label(address, ins) {
722 self.labels.insert(label);
723 self.last_conditional_destination = self.last_conditional_destination.max(Some(label));
724 }
725
726 if self.jump_table_state.is_numerical_jump_offset() {
727 return ParseFunctionState::Continue;
729 }
730
731 let ins_size = if let Ins::Thumb(thumb_ins) = ins {
732 if thumb_ins.op != thumb::Opcode::Bl && thumb_ins.op != thumb::Opcode::BlxI {
733 2
735 } else if matches!(parsed_ins.args[0], Argument::BranchDest(_)) {
736 4
738 } else {
739 return ParseFunctionState::IllegalIns { address, ins };
741 }
742 } else {
743 4
745 };
746
747 self.illegal_code_state = self.illegal_code_state.handle(ins, parsed_ins);
748 if self.illegal_code_state.is_illegal() {
749 return ParseFunctionState::IllegalIns { address, ins };
750 }
751
752 let in_conditional_block = Some(address) < self.last_conditional_destination;
753 let is_return = self.is_return(
754 ins,
755 parsed_ins,
756 address,
757 self.start_address,
758 self.module_start_address,
759 self.module_end_address,
760 );
761 if !in_conditional_block && is_return {
762 let end_address = address + ins_size;
763 if let Some(destination) = Function::is_branch(ins, parsed_ins, address) {
764 let outside_function =
765 destination < self.start_address || destination >= end_address;
766 if outside_function {
767 self.function_calls.insert(address, CalledFunction {
769 ins,
770 address: destination,
771 thumb: self.thumb,
772 });
773 }
774 }
775
776 self.end_address = Some(address + ins_size);
778 return ParseFunctionState::Done;
779 }
780
781 if address > self.start_address
782 && Function::is_entry_instruction(ins, parsed_ins)
783 && let Some(prev_ins) = self.prev_ins
784 && let Some(prev_parsed_ins) = self.prev_parsed_ins.as_ref()
785 && let Some(prev_address) = self.prev_address
786 && Function::is_branch(prev_ins, prev_parsed_ins, prev_address).is_some()
787 {
788 let is_conditional = in_conditional_block || prev_ins.is_conditional();
789 if is_conditional {
790 self.end_address = Some(address);
792 return ParseFunctionState::Done;
793 }
794 }
795
796 self.function_branch_state = self.function_branch_state.handle(ins, parsed_ins);
797 if let Some(destination) = Function::is_branch(ins, parsed_ins, address) {
798 let in_current_module =
799 destination >= self.module_start_address && destination < self.module_end_address;
800 if !in_current_module {
801 self.function_calls.insert(address, CalledFunction {
803 ins,
804 address: destination,
805 thumb: self.thumb,
806 });
807 } else if self.function_branch_state.is_function_branch()
808 || self
809 .existing_functions
810 .map(|functions| functions.contains_key(&destination))
811 .unwrap_or(false)
812 {
813 if !ins.is_conditional() && !in_conditional_block {
814 self.end_address = Some(address + ins_size);
816 return ParseFunctionState::Done;
817 } else {
818 self.function_calls.insert(address, CalledFunction {
821 ins,
822 address: destination,
823 thumb: self.thumb,
824 });
825 }
826 } else {
827 if let Some(state) = self.handle_label(destination, address, parser, ins_size) {
829 return state;
830 }
831 }
832 }
833
834 if let Some(pool_address) = Function::is_pool_load(ins, parsed_ins, address, self.thumb) {
835 self.pool_constants.insert(pool_address);
836 self.last_pool_address = self.last_pool_address.max(Some(pool_address));
837 }
838
839 self.inline_table_state = self.inline_table_state.handle(self.thumb, address, parsed_ins);
840 if let Some(table) = self.inline_table_state.get_table() {
841 log::debug!("Inline table found at {:#x}, size {:#x}", table.address, table.size);
842 self.inline_tables.insert(table.address, table);
843 }
844
845 if let Some(called_function) =
846 Function::is_function_call(ins, parsed_ins, address, self.thumb)
847 {
848 self.function_calls.insert(address, called_function);
849 }
850
851 if self.check_defs_uses && !Self::is_nop(ins, parsed_ins) {
852 if Self::is_push(ins) {
853 ins.register_list().iter().for_each(|reg| {
855 self.defined_registers.insert(reg);
856 });
857 }
858
859 let defs_uses = match ins {
861 Ins::Arm(ins) => {
862 Some((ins.defs(&Default::default()), ins.uses(&Default::default())))
863 }
864 Ins::Thumb(ins) => {
865 Some((ins.defs(&Default::default()), ins.uses(&Default::default())))
866 }
867 Ins::Data => None,
868 };
869 if let Some((defs, uses)) = defs_uses {
870 for usage in uses {
871 let legal = match usage {
872 Argument::Reg(reg) => {
873 if let Ins::Arm(ins) = ins
874 && ins.op == arm::Opcode::Str
875 && ins.field_rn_deref().reg == Register::Sp
876 {
877 self.defined_registers.insert(reg.reg);
880 continue;
881 }
882
883 self.defined_registers.contains(®.reg)
884 }
885 Argument::RegList(reg_list) => {
886 reg_list.iter().all(|reg| self.defined_registers.contains(®))
887 }
888 Argument::ShiftReg(shift_reg) => {
889 self.defined_registers.contains(&shift_reg.reg)
890 }
891 Argument::OffsetReg(offset_reg) => {
892 self.defined_registers.contains(&offset_reg.reg)
893 }
894 _ => continue,
895 };
896 if !legal {
897 return ParseFunctionState::IllegalIns { address, ins };
898 }
899 }
900 if !is_return {
901 for def in defs {
902 match def {
903 Argument::Reg(reg) => {
904 self.defined_registers.insert(reg.reg);
905 }
906 Argument::RegList(reg_list) => {
907 for reg in reg_list.iter() {
908 self.defined_registers.insert(reg);
909 }
910 }
911 Argument::ShiftReg(shift_reg) => {
912 self.defined_registers.insert(shift_reg.reg);
913 }
914 Argument::OffsetReg(offset_reg) => {
915 self.defined_registers.insert(offset_reg.reg);
916 }
917 _ => continue,
918 };
919 }
920 }
921 }
922 }
923
924 ParseFunctionState::Continue
925 }
926
927 pub fn handle_ins(
928 &mut self,
929 parser: &mut Parser,
930 address: u32,
931 ins: Ins,
932 parsed_ins: ParsedIns,
933 ) -> ParseFunctionState {
934 let state = self.handle_ins_inner(parser, address, ins, &parsed_ins);
935 self.prev_ins = Some(ins);
936 self.prev_parsed_ins = Some(parsed_ins);
937 self.prev_address = Some(address);
938 state
939 }
940
941 fn handle_label(
942 &mut self,
943 destination: u32,
944 address: u32,
945 parser: &mut Parser,
946 ins_size: u32,
947 ) -> Option<ParseFunctionState> {
948 self.labels.insert(destination);
949 self.last_conditional_destination =
950 self.last_conditional_destination.max(Some(destination));
951
952 let next_address = address + ins_size;
953 if self.pool_constants.contains(&next_address) {
954 let branch_backwards = destination <= address;
955
956 if let Some(after_pools) = self.labels.range(address + 1..).next().copied() {
964 if after_pools > address + 0x1000 {
965 log::warn!(
966 "Massive gap from constant pool at {:#x} to next label at {:#x}",
967 next_address,
968 after_pools
969 );
970 }
971 parser.seek_forward(after_pools);
972 } else if !branch_backwards {
973 self.end_address = Some(next_address);
976 return Some(ParseFunctionState::Done);
977 } else {
978 let after_pools = (next_address..)
979 .step_by(4)
980 .find(|addr| !self.pool_constants.contains(addr))
981 .unwrap();
982 log::warn!(
983 "No label past constant pool at {:#x}, jumping to first address not occupied by a pool constant ({:#x})",
984 next_address,
985 after_pools
986 );
987 parser.seek_forward(after_pools);
988 }
989 }
990
991 None
992 }
993
994 fn into_function(self, state: ParseFunctionState) -> Result<Function, IntoFunctionError> {
995 match state {
996 ParseFunctionState::Continue => {
997 return NotDoneSnafu.fail();
998 }
999 ParseFunctionState::IllegalIns { address, ins } => {
1000 return IllegalInsSnafu { address, ins }.fail()?;
1001 }
1002 ParseFunctionState::Done => {}
1003 };
1004 let Some(end_address) = self.end_address else {
1005 return NoEpilogueSnafu.fail()?;
1006 };
1007
1008 let end_address = self
1009 .known_end_address
1010 .unwrap_or(end_address.max(self.last_pool_address.map(|a| a + 4).unwrap_or(0)));
1011 if end_address > self.module_end_address {
1012 return NoEpilogueSnafu.fail()?;
1013 }
1014
1015 Ok(Function {
1016 name: self.name,
1017 start_address: self.start_address,
1018 end_address,
1019 first_instruction_address: self.start_address,
1020 thumb: self.thumb,
1021 labels: self.labels,
1022 pool_constants: self.pool_constants,
1023 jump_tables: self.jump_tables,
1024 inline_tables: self.inline_tables,
1025 function_calls: self.function_calls,
1026 })
1027 }
1028
1029 fn is_return(
1030 &self,
1031 ins: Ins,
1032 parsed_ins: &ParsedIns,
1033 address: u32,
1034 function_start: u32,
1035 module_start_address: u32,
1036 module_end_address: u32,
1037 ) -> bool {
1038 if ins.is_conditional() {
1039 return false;
1040 }
1041
1042 let args = &parsed_ins.args;
1043 match (parsed_ins.mnemonic, args[0], args[1]) {
1044 ("bx", _, _) => true,
1046 ("mov", Argument::Reg(Reg { reg: Register::Pc, .. }), _) => true,
1048 ("ldmia", _, Argument::RegList(reg_list)) if reg_list.contains(Register::Pc) => true,
1050 ("pop", Argument::RegList(reg_list), _) if reg_list.contains(Register::Pc) => true,
1052 ("b", Argument::BranchDest(offset), _) if offset < 0 => {
1054 Function::is_branch(ins, parsed_ins, address)
1056 .map(|destination| {
1057 destination >= function_start
1058 || destination < module_start_address
1059 || destination >= module_end_address
1060 })
1061 .unwrap_or(false)
1062 }
1063 (
1065 "subs",
1066 Argument::Reg(Reg { reg: Register::Pc, .. }),
1067 Argument::Reg(Reg { reg: Register::Lr, .. }),
1068 ) => true,
1069 ("ldr", Argument::Reg(Reg { reg: Register::Pc, .. }), _) => true,
1071 _ => false,
1072 }
1073 }
1074
1075 fn is_nop(ins: Ins, parsed_ins: &ParsedIns) -> bool {
1076 match (ins.mnemonic(), parsed_ins.args[0], parsed_ins.args[1], parsed_ins.args[2]) {
1077 ("nop", _, _, _) => true,
1078 (
1079 "mov",
1080 Argument::Reg(Reg { reg: dest, .. }),
1081 Argument::Reg(Reg { reg: src, .. }),
1082 Argument::None,
1083 ) => dest == src,
1084 _ => false,
1085 }
1086 }
1087
1088 fn is_push(ins: Ins) -> bool {
1089 match ins {
1090 Ins::Arm(arm_ins) => {
1091 if arm_ins.op == arm::Opcode::StmW && arm_ins.field_rn_wb().reg == Register::Sp {
1092 true
1093 } else {
1094 matches!(arm_ins.op, arm::Opcode::PushM | arm::Opcode::PushR)
1095 }
1096 }
1097 Ins::Thumb(thumb_ins) => thumb_ins.op == thumb::Opcode::Push,
1098 _ => false,
1099 }
1100 }
1101}
1102
1103#[derive(Default)]
1104pub struct ParseFunctionOptions {
1105 pub thumb: Option<bool>,
1107}
1108
1109enum ParseFunctionState {
1110 Continue,
1111 IllegalIns { address: u32, ins: Ins },
1112 Done,
1113}
1114
1115impl ParseFunctionState {
1116 pub fn ended(&self) -> bool {
1117 match self {
1118 Self::Continue => false,
1119 Self::IllegalIns { .. } | Self::Done => true,
1120 }
1121 }
1122}
1123
1124#[derive(Debug, Snafu)]
1125pub enum ParseFunctionError {
1126 #[snafu(display("Illegal instruction at {address:#010x}: {ins:?}"))]
1127 IllegalIns { address: u32, ins: Ins },
1128 #[snafu(display("No epilogue found"))]
1129 NoEpilogue,
1130 #[snafu(display("Illegal function start at {address:#010x}: {ins}"))]
1131 InvalidStart { address: u32, ins: DisplayIns },
1132}
1133
1134#[derive(Debug)]
1135pub struct DisplayIns(Ins);
1136
1137impl From<Ins> for DisplayIns {
1138 fn from(value: Ins) -> Self {
1139 Self(value)
1140 }
1141}
1142
1143impl Display for DisplayIns {
1144 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1145 let parsed_ins = match self.0 {
1146 Ins::Arm(ins) => ins.parse(&PARSE_FLAGS),
1147 Ins::Thumb(ins) => ins.parse(&PARSE_FLAGS),
1148 Ins::Data => return write!(f, "<data>"),
1149 };
1150 write!(f, "{}", parsed_ins.display(Default::default()))
1151 }
1152}
1153
1154#[derive(Default)]
1155pub struct FunctionSearchOptions<'a> {
1156 pub start_address: Option<u32>,
1158 pub last_function_address: Option<u32>,
1160 pub end_address: Option<u32>,
1162 pub max_function_start_search_distance: u32,
1165 pub use_data_as_upper_bound: bool,
1167 pub function_addresses: Option<BTreeSet<u32>>,
1171 pub existing_functions: Option<&'a BTreeMap<u32, Function>>,
1176 pub check_defs_uses: bool,
1178}
1179
1180#[derive(Clone, Copy, Debug)]
1181pub struct CalledFunction {
1182 pub ins: Ins,
1183 pub address: u32,
1184 pub thumb: bool,
1185}
1186
1187pub struct PoolConstant {
1188 pub address: u32,
1189 pub value: u32,
1190}