Skip to content

Commit

Permalink
Support block_relocations and add_relocations in config.yml
Browse files Browse the repository at this point in the history
This allows more granular control over generated relocations.

Also optimizes relocation address validity checks,
leading to ~20% faster relocation analysis.

Config example:
```
block_relocations:
# Block any relocation pointing to this address.
- target: .data:0x80130140
# Block any relocation originating from this address.
- source: .text:0x80047160
  # (optional) End address to make it a range.
  end: .text:0x800471A8

add_relocations:
# Inserts or overwrites a relocation.
# From: `subi r3, r3, 0x7657`
# To: `li r3, mesWInsert-0x1@sda21`
- source: .text:0x800473F4
  type: sda21
  target: mesWInsert
  addend: -1
```

Resolves #33
Resolves #52
  • Loading branch information
encounter committed May 20, 2024
1 parent e1c8065 commit c3f3ea5
Show file tree
Hide file tree
Showing 7 changed files with 449 additions and 106 deletions.
71 changes: 32 additions & 39 deletions src/analysis/tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ impl Tracker {
Ok(ExecCbResult::Continue)
}
StepResult::LoadStore { address, source, source_reg } => {
if self.is_valid_section_address(obj, ins_addr) {
if !obj.blocked_relocation_sources.contains(ins_addr) {
if (source_reg == 2
&& matches!(self.sda2_base, Some(v) if source.value == GprValue::Constant(v)))
|| (source_reg == 13
Expand Down Expand Up @@ -503,28 +503,12 @@ impl Tracker {
Ok(())
}

fn is_valid_section_address(&self, obj: &ObjInfo, from: SectionAddress) -> bool {
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).next_back() {
if from.section == start.section && from.address >= start.address && from.address < end
{
return false;
}
}
true
}

fn is_valid_address(
&self,
obj: &ObjInfo,
from: SectionAddress,
addr: u32,
) -> Option<SectionAddress> {
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).next_back() {
if from.section == start.section && from.address >= start.address && from.address < end
{
return None;
}
}
// Check for an existing relocation
if cfg!(debug_assertions) {
let relocation_target = relocation_target_for(obj, from, None).ok().flatten();
Expand All @@ -537,33 +521,42 @@ impl Tracker {
if obj.kind == ObjKind::Relocatable {
return None;
}
if self.known_relocations.contains(&from) {
let section_index =
obj.sections.at_address(addr).ok().map(|(idx, _)| idx).unwrap_or(usize::MAX);
return Some(SectionAddress::new(section_index, addr));
}
if self.stack_address == Some(addr)
|| self.stack_end == Some(addr)
|| self.db_stack_addr == Some(addr)
|| self.arena_lo == Some(addr)
|| self.arena_hi == Some(addr)
|| self.sda2_base == Some(addr)
|| self.sda_base == Some(addr)
{
let section_index =
obj.sections.at_address(addr).ok().map(|(idx, _)| idx).unwrap_or(usize::MAX);
return Some(SectionAddress::new(section_index, addr));
// Check blocked relocation sources
if obj.blocked_relocation_sources.contains(from) {
return None;
}
// if addr > 0x80000000 && addr < 0x80003100 {
// return true;
// }
// Find the section containing the address
if let Ok((section_index, section)) = obj.sections.at_address(addr) {
// References to code sections will never be unaligned
if section.kind != ObjSectionKind::Code || addr & 3 == 0 {
return Some(SectionAddress::new(section_index, addr));
if section.kind == ObjSectionKind::Code && addr & 3 != 0 {
return None;
}
let section_address = SectionAddress::new(section_index, addr);
// Check blocked relocation targets
if obj.blocked_relocation_targets.contains(section_address) {
return None;
}
// It's valid
Some(section_address)
} else {
// Check known relocations (function signature matching)
if self.known_relocations.contains(&from) {
return Some(SectionAddress::new(usize::MAX, addr));
}
// Check special symbols
if self.stack_address == Some(addr)
|| self.stack_end == Some(addr)
|| self.db_stack_addr == Some(addr)
|| self.arena_lo == Some(addr)
|| self.arena_hi == Some(addr)
|| self.sda2_base == Some(addr)
|| self.sda_base == Some(addr)
{
return Some(SectionAddress::new(usize::MAX, addr));
}
// Not valid
None
}
None
}

fn special_symbol(
Expand Down
172 changes: 126 additions & 46 deletions src/cmd/dol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::{

use anyhow::{anyhow, bail, Context, Result};
use argp::FromArgs;
use cwdemangle::demangle;
use itertools::Itertools;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -40,8 +41,8 @@ use crate::{
bin2c::bin2c,
comment::MWComment,
config::{
apply_splits_file, apply_symbols_file, is_auto_symbol, write_splits_file,
write_symbols_file,
apply_splits_file, apply_symbols_file, is_auto_symbol, signed_hex_serde,
write_splits_file, write_symbols_file, SectionAddressRef,
},
dep::DepFile,
dol::process_dol,
Expand Down Expand Up @@ -236,7 +237,27 @@ pub struct ProjectConfig {
pub export_all: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
impl Default for ProjectConfig {
fn default() -> Self {
Self {
base: Default::default(),
selfile: None,
selfile_hash: None,
mw_comment_version: None,
quick_analysis: false,
modules: vec![],
detect_objects: true,
detect_strings: true,
write_asm: true,
common_start: None,
symbols_known: false,
fill_gaps: true,
export_all: true,
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct ModuleConfig {
/// Object name. If not specified, the file name without extension will be used.
#[serde(skip_serializing_if = "is_default")]
Expand All @@ -261,6 +282,10 @@ pub struct ModuleConfig {
pub links: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub extract: Vec<ExtractConfig>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub block_relocations: Vec<BlockRelocationConfig>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub add_relocations: Vec<AddRelocationConfig>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
Expand All @@ -277,6 +302,36 @@ pub struct ExtractConfig {
pub header: Option<PathBuf>,
}

/// A relocation that should be blocked.
/// Only one of `source` or `target` should be specified.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct BlockRelocationConfig {
/// Match by the address of the relocation.
/// Format: `section:address`, e.g. `.text:0x80001234`.
pub source: Option<SectionAddressRef>,
/// Match by the address of the relocation target.
/// Format: `section:address`, e.g. `.text:0x80001234`.
pub target: Option<SectionAddressRef>,
/// An optional end address for the (exclusive) range.
/// Format: `section:address`, e.g. `.text:0x80001234`.
pub end: Option<SectionAddressRef>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct AddRelocationConfig {
/// The address of the relocation to add.
/// Format: `section:address`, e.g. `.text:0x80001234`.
pub source: SectionAddressRef,
/// The relocation type to add.
#[serde(rename = "type")]
pub kind: ObjRelocKind,
/// The target symbol name.
pub target: String,
/// The addend for the relocation. (optional)
#[serde(with = "signed_hex_serde", default, skip_serializing_if = "is_default")]
pub addend: i64,
}

impl ModuleConfig {
pub fn file_name(&self) -> Cow<'_, str> {
self.object.file_name().unwrap_or(self.object.as_os_str()).to_string_lossy()
Expand Down Expand Up @@ -763,6 +818,9 @@ fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
None
};

// Apply block relocations from config
apply_block_relocations(&mut obj, &config.base.block_relocations)?;

if !config.symbols_known {
// TODO move before symbols?
debug!("Performing signature analysis");
Expand Down Expand Up @@ -793,6 +851,9 @@ fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
// Create _ctors and _dtors symbols if missing
update_ctors_dtors(&mut obj)?;

// Apply additional relocations from config
apply_add_relocations(&mut obj, &config.base.add_relocations)?;

Ok(AnalyzeResult { obj, dep, symbols_cache, splits_cache })
}

Expand Down Expand Up @@ -988,6 +1049,9 @@ fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Res
None
};

// Apply block relocations from config
apply_block_relocations(&mut module_obj, &module_config.block_relocations)?;

if !config.symbols_known {
debug!("Analyzing module {}", module_obj.module_id);
if !config.quick_analysis {
Expand All @@ -1007,6 +1071,9 @@ fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Res
// Determine REL section alignment
update_rel_section_alignment(&mut module_obj, &header)?;

// Apply additional relocations from config
apply_add_relocations(&mut module_obj, &module_config.add_relocations)?;

Ok(AnalyzeResult { obj: module_obj, dep, symbols_cache, splits_cache })
}

Expand Down Expand Up @@ -1750,33 +1817,7 @@ fn apply(args: ApplyArgs) -> Result<()> {
}

fn config(args: ConfigArgs) -> Result<()> {
let mut config = ProjectConfig {
base: ModuleConfig {
name: None,
object: Default::default(),
hash: None,
splits: None,
symbols: None,
map: None,
force_active: vec![],
ldscript_template: None,
links: None,
extract: vec![],
},
selfile: None,
selfile_hash: None,
mw_comment_version: None,
quick_analysis: false,
modules: vec![],
detect_objects: true,
detect_strings: true,
write_asm: true,
common_start: None,
symbols_known: false,
fill_gaps: true,
export_all: true,
};

let mut config = ProjectConfig::default();
let mut modules = Vec::<(u32, ModuleConfig)>::new();
for result in FileIterator::new(&args.objects)? {
let (path, entry) = result?;
Expand All @@ -1790,16 +1831,9 @@ fn config(args: ConfigArgs) -> Result<()> {
Some(ext) if ext.eq_ignore_ascii_case(OsStr::new("rel")) => {
let header = process_rel_header(&mut entry.as_reader())?;
modules.push((header.module_id, ModuleConfig {
name: None,
object: path,
hash: Some(file_sha1_string(&mut entry.as_reader())?),
splits: None,
symbols: None,
map: None,
force_active: vec![],
ldscript_template: None,
links: None,
extract: vec![],
..Default::default()
}));
}
Some(ext) if ext.eq_ignore_ascii_case(OsStr::new("sel")) => {
Expand All @@ -1808,16 +1842,9 @@ fn config(args: ConfigArgs) -> Result<()> {
}
Some(ext) if ext.eq_ignore_ascii_case(OsStr::new("rso")) => {
config.modules.push(ModuleConfig {
name: None,
object: path,
hash: Some(file_sha1_string(&mut entry.as_reader())?),
splits: None,
symbols: None,
map: None,
force_active: vec![],
ldscript_template: None,
links: None,
extract: vec![],
..Default::default()
});
}
_ => bail!("Unknown file extension: '{}'", path.display()),
Expand All @@ -1834,3 +1861,56 @@ fn config(args: ConfigArgs) -> Result<()> {
out.flush()?;
Ok(())
}

/// Applies the blocked relocation ranges from module config `blocked_relocations`
fn apply_block_relocations(
obj: &mut ObjInfo,
block_relocations: &[BlockRelocationConfig],
) -> Result<()> {
for reloc in block_relocations {
let end = reloc.end.as_ref().map(|end| end.resolve(obj)).transpose()?;
match (&reloc.source, &reloc.target) {
(Some(_), Some(_)) => {
bail!("Cannot specify both source and target for blocked relocation");
}
(Some(source), None) => {
let start = source.resolve(obj)?;
obj.blocked_relocation_sources.insert(start, end.unwrap_or(start + 1));
}
(None, Some(target)) => {
let start = target.resolve(obj)?;
obj.blocked_relocation_targets.insert(start, end.unwrap_or(start + 1));
}
(None, None) => {
bail!("Blocked relocation must specify either source or target");
}
}
}
Ok(())
}

/// Applies the relocations from module config `add_relocations`.
fn apply_add_relocations(obj: &mut ObjInfo, relocations: &[AddRelocationConfig]) -> Result<()> {
for reloc in relocations {
let SectionAddress { section, address } = reloc.source.resolve(obj)?;
let (target_symbol, _) = match obj.symbols.by_name(&reloc.target)? {
Some(v) => v,
None => {
// Assume external symbol
let symbol_index = obj.symbols.add_direct(ObjSymbol {
name: reloc.target.clone(),
demangled_name: demangle(&reloc.target, &Default::default()),
..Default::default()
})?;
(symbol_index, &obj.symbols[symbol_index])
}
};
obj.sections[section].relocations.replace(address, ObjReloc {
kind: reloc.kind,
target_symbol,
addend: reloc.addend,
module: None,
});
}
Ok(())
}
Loading

0 comments on commit c3f3ea5

Please sign in to comment.