Support block_relocations and add_relocations in config.yml

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
This commit is contained in:
2024-05-19 22:49:40 -06:00
parent e1c80655b7
commit c3f3ea58e8
7 changed files with 449 additions and 106 deletions

100
src/obj/addresses.rs Normal file
View File

@@ -0,0 +1,100 @@
use crate::analysis::cfa::SectionAddress;
/// A collection of address ranges.
/// Slow to insert, but fast to check if an address is contained in any of the ranges.
#[derive(Debug, Clone)]
pub struct AddressRanges {
/// (start, end) pairs of addresses.
inner: Vec<(SectionAddress, u32)>,
}
impl Default for AddressRanges {
fn default() -> Self { Self::new() }
}
impl AddressRanges {
#[inline]
pub fn new() -> Self { Self { inner: vec![] } }
pub fn insert(&mut self, start: SectionAddress, end: SectionAddress) {
debug_assert_eq!(
start.section, end.section,
"AddressIntervals::insert: start and end must be in the same section"
);
// TODO: Handle overlapping ranges?
match self.inner.binary_search_by_key(&start, |&(start, _)| start) {
Ok(pos) => {
let (_, end_ref) = &mut self.inner[pos];
*end_ref = end.address.max(*end_ref);
}
Err(pos) => self.inner.insert(pos, (start, end.address)),
}
}
pub fn contains(&self, address: SectionAddress) -> bool {
let pos = match self.inner.binary_search_by_key(&address, |&(start, _)| start) {
Ok(_) => return true,
Err(pos) => pos,
};
if pos == 0 {
return false;
}
let (start, end) = &self.inner[pos - 1];
start.section == address.section
&& address.address >= start.address
&& address.address < *end
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_contains() {
let mut intervals = AddressRanges::new();
intervals.insert(SectionAddress { section: 0, address: 0x80000000 }, SectionAddress {
section: 0,
address: 0x80000004,
});
intervals.insert(SectionAddress { section: 0, address: 0x80000008 }, SectionAddress {
section: 0,
address: 0x8000000C,
});
intervals.insert(SectionAddress { section: 12, address: 0x80004000 }, SectionAddress {
section: 12,
address: 0x80004004,
});
intervals.insert(SectionAddress { section: 12, address: 0x80004008 }, SectionAddress {
section: 12,
address: 0x8000400C,
});
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000000 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000001 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000002 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000003 }));
assert!(!intervals.contains(SectionAddress { section: 0, address: 0x80000004 }));
assert!(!intervals.contains(SectionAddress { section: 0, address: 0x80000005 }));
assert!(!intervals.contains(SectionAddress { section: 0, address: 0x80000006 }));
assert!(!intervals.contains(SectionAddress { section: 0, address: 0x80000007 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000008 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x80000009 }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x8000000A }));
assert!(intervals.contains(SectionAddress { section: 0, address: 0x8000000B }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004000 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004001 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004002 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004003 }));
assert!(!intervals.contains(SectionAddress { section: 12, address: 0x80004004 }));
assert!(!intervals.contains(SectionAddress { section: 12, address: 0x80004005 }));
assert!(!intervals.contains(SectionAddress { section: 12, address: 0x80004006 }));
assert!(!intervals.contains(SectionAddress { section: 12, address: 0x80004007 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004008 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x80004009 }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x8000400A }));
assert!(intervals.contains(SectionAddress { section: 12, address: 0x8000400B }));
assert!(!intervals.contains(SectionAddress { section: 12, address: 0x8000400C }));
}
}

View File

@@ -1,3 +1,4 @@
mod addresses;
mod relocations;
mod sections;
mod splits;
@@ -21,6 +22,7 @@ pub use symbols::{
use crate::{
analysis::cfa::SectionAddress,
obj::addresses::AddressRanges,
util::{comment::MWComment, rel::RelReloc},
};
@@ -69,7 +71,8 @@ pub struct ObjInfo {
// Extracted
pub link_order: Vec<ObjUnit>,
pub blocked_ranges: BTreeMap<SectionAddress, u32>, // start -> end
pub blocked_relocation_sources: AddressRanges,
pub blocked_relocation_targets: AddressRanges,
// From .ctors, .dtors and extab
pub known_functions: BTreeMap<SectionAddress, Option<u32>>,
@@ -105,7 +108,8 @@ impl ObjInfo {
arena_lo: None,
arena_hi: None,
link_order: vec![],
blocked_ranges: Default::default(),
blocked_relocation_sources: Default::default(),
blocked_relocation_targets: Default::default(),
known_functions: Default::default(),
module_id: 0,
unresolved_relocations: vec![],

View File

@@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::obj::SymbolIndex;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ObjRelocKind {
Absolute,
PpcAddr16Hi,
@@ -21,6 +21,39 @@ pub enum ObjRelocKind {
PpcEmbSda21,
}
impl Serialize for ObjRelocKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
serializer.serialize_str(match self {
ObjRelocKind::Absolute => "abs",
ObjRelocKind::PpcAddr16Hi => "hi",
ObjRelocKind::PpcAddr16Ha => "ha",
ObjRelocKind::PpcAddr16Lo => "l",
ObjRelocKind::PpcRel24 => "rel24",
ObjRelocKind::PpcRel14 => "rel14",
ObjRelocKind::PpcEmbSda21 => "sda21",
})
}
}
impl<'de> Deserialize<'de> for ObjRelocKind {
fn deserialize<D>(deserializer: D) -> Result<ObjRelocKind, D::Error>
where D: serde::Deserializer<'de> {
match String::deserialize(deserializer)?.as_str() {
"Absolute" | "abs" => Ok(ObjRelocKind::Absolute),
"PpcAddr16Hi" | "hi" => Ok(ObjRelocKind::PpcAddr16Hi),
"PpcAddr16Ha" | "ha" => Ok(ObjRelocKind::PpcAddr16Ha),
"PpcAddr16Lo" | "l" => Ok(ObjRelocKind::PpcAddr16Lo),
"PpcRel24" | "rel24" => Ok(ObjRelocKind::PpcRel24),
"PpcRel14" | "rel14" => Ok(ObjRelocKind::PpcRel14),
"PpcEmbSda21" | "sda21" => Ok(ObjRelocKind::PpcEmbSda21),
s => Err(serde::de::Error::unknown_variant(s, &[
"abs", "hi", "ha", "l", "rel24", "rel14", "sda21",
])),
}
}
}
#[derive(Debug, Clone)]
pub struct ObjReloc {
pub kind: ObjRelocKind,

View File

@@ -49,6 +49,8 @@ flags! {
Stripped,
/// Disable automatic export of symbol
NoExport,
/// Symbol does not contain any relocations
NoReloc,
}
}
@@ -99,6 +101,9 @@ impl ObjSymbolFlagSet {
#[inline]
pub fn is_no_export(&self) -> bool { self.0.contains(ObjSymbolFlags::NoExport) }
#[inline]
pub fn is_no_reloc(&self) -> bool { self.0.contains(ObjSymbolFlags::NoReloc) }
#[inline]
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
match scope {
@@ -137,7 +142,8 @@ impl ObjSymbolFlagSet {
| ObjSymbolFlags::NoWrite
| ObjSymbolFlags::RelocationIgnore
| ObjSymbolFlags::Stripped
| ObjSymbolFlags::NoExport)
| ObjSymbolFlags::NoExport
| ObjSymbolFlags::NoReloc)
}
}