use std::{collections::BTreeMap, ops::Range};
use snafu::Snafu;
use crate::{
analysis::functions::Function,
config::{
Comments,
module::{AnalysisOptions, ModuleKind},
relocations::{
Relocation, RelocationKind, RelocationModule, RelocationOptions, Relocations,
RelocationsError,
},
section::{Section, SectionKind, Sections},
symbol::{SymBss, SymData, SymbolMap, SymbolMapError},
},
function,
};
pub struct FindLocalDataOptions<'a> {
pub sections: &'a Sections,
pub module_kind: ModuleKind,
pub symbol_map: &'a mut SymbolMap,
pub relocations: &'a mut Relocations,
pub name_prefix: &'a str,
pub code: &'a [u8],
pub base_address: u32,
pub address_range: Option<Range<u32>>,
pub relocation_overrides: &'a BTreeMap<u32, RelocationKind>,
}
#[derive(Debug, Snafu)]
pub enum FindLocalDataError {
#[snafu(transparent)]
SymbolMap { source: SymbolMapError },
#[snafu(transparent)]
Relocations { source: RelocationsError },
}
pub fn find_local_data_from_pools(
function: &Function,
options: FindLocalDataOptions,
analysis_options: &AnalysisOptions,
) -> Result<(), FindLocalDataError> {
let FindLocalDataOptions {
sections,
module_kind,
symbol_map,
relocations,
name_prefix,
code,
base_address,
relocation_overrides,
..
} = options;
let address_range = None;
for pool_constant in function.iter_pool_constants(code, base_address) {
let pointer = pool_constant.value;
if let Some(reloc_kind) = relocation_overrides.get(&pointer) {
relocations.add(Relocation::new(RelocationOptions {
from: pool_constant.address,
to: pointer,
addend: 0,
kind: *reloc_kind,
module: RelocationModule::from(module_kind),
comments: Comments::new(),
}))?;
continue;
}
let Some((_, section)) = sections.get_by_contained_address(pointer) else {
continue;
};
let function = symbol_map.get_function(pointer & !1)?;
if section.kind() == SectionKind::Code
&& let Some((function, _)) = function
{
let thumb = (pointer & 1) != 0;
if function.mode.into_thumb() != Some(thumb) {
continue;
}
let reloc =
relocations.add_load(pool_constant.address, pointer, 0, module_kind.into())?;
if analysis_options.provide_reloc_source {
reloc.comments.post_comment = Some(function!().to_string());
}
} else {
add_symbol_from_pointer(
section,
pool_constant.address,
pointer,
FindLocalDataOptions {
sections,
module_kind,
symbol_map,
relocations,
name_prefix,
code,
base_address,
address_range: address_range.clone(),
relocation_overrides,
},
analysis_options,
)?;
}
}
Ok(())
}
pub fn find_local_data_from_section(
section: &Section,
options: FindLocalDataOptions,
analysis_options: &AnalysisOptions,
) -> Result<(), FindLocalDataError> {
let FindLocalDataOptions {
sections,
module_kind,
symbol_map,
relocations,
name_prefix,
code,
base_address,
relocation_overrides,
..
} = options;
let address_range = options.address_range.clone().unwrap_or(section.address_range());
for word in section.iter_words(code, Some(address_range.clone())) {
let pointer = word.value;
if let Some(reloc_kind) = relocation_overrides.get(&pointer) {
relocations.add(Relocation::new(RelocationOptions {
from: word.address,
to: pointer,
addend: 0,
kind: *reloc_kind,
module: RelocationModule::from(module_kind),
comments: Comments::new(),
}))?;
continue;
}
let Some((_, section)) = options.sections.get_by_contained_address(pointer) else {
continue;
};
add_symbol_from_pointer(
section,
word.address,
pointer,
FindLocalDataOptions {
sections,
module_kind,
symbol_map,
relocations,
name_prefix,
code,
base_address,
address_range: Some(address_range.clone()),
relocation_overrides,
},
analysis_options,
)?;
}
Ok(())
}
fn add_symbol_from_pointer(
section: &Section,
address: u32,
pointer: u32,
options: FindLocalDataOptions,
analysis_options: &AnalysisOptions,
) -> Result<(), FindLocalDataError> {
let FindLocalDataOptions { module_kind, symbol_map, relocations, name_prefix, .. } = options;
let name = format!("{name_prefix}{pointer:08x}");
let reloc = match section.kind() {
SectionKind::Code => {
let thumb = (pointer & 1) != 0;
if let Some((function, _)) = symbol_map.get_function(pointer)? {
if function.mode.into_thumb() == Some(thumb) {
relocations.add_load(address, pointer, 0, module_kind.into())?
} else {
return Ok(());
}
} else {
return Ok(());
}
}
SectionKind::Data | SectionKind::Rodata => {
symbol_map.add_data(Some(name), pointer, SymData::Any)?;
relocations.add_load(address, pointer, 0, module_kind.into())?
}
SectionKind::Bss => {
symbol_map.add_bss(Some(name), pointer, SymBss { size: None })?;
relocations.add_load(address, pointer, 0, module_kind.into())?
}
};
if analysis_options.provide_reloc_source {
reloc.comments.post_comment = Some(function!().to_string());
}
Ok(())
}