ds_decomp/config/
section.rs

1use std::{
2    backtrace::Backtrace,
3    collections::{BTreeMap, HashMap},
4    fmt::Display,
5    num::ParseIntError,
6    ops::Range,
7};
8
9use serde::Serialize;
10use snafu::Snafu;
11
12use crate::{
13    analysis::functions::Function,
14    util::{bytes::FromSlice, parse::parse_u32},
15};
16
17use super::{iter_attributes, module::Module, ParseContext};
18
19pub const DTCM_SECTION: &str = ".dtcm";
20
21#[derive(Clone, Copy)]
22pub struct SectionIndex(pub usize);
23
24#[derive(Clone)]
25pub struct Section {
26    name: String,
27    kind: SectionKind,
28    start_address: u32,
29    end_address: u32,
30    alignment: u32,
31    functions: BTreeMap<u32, Function>,
32}
33
34#[derive(Debug, Snafu)]
35pub enum SectionError {
36    #[snafu(display(
37        "Section {name} must not end ({end_address:#010x}) before it starts ({start_address:#010x}):\n{backtrace}"
38    ))]
39    EndBeforeStart { name: String, start_address: u32, end_address: u32, backtrace: Backtrace },
40    #[snafu(display("Section {name} aligment ({alignment}) must be a power of two:\n{backtrace}"))]
41    AlignmentPowerOfTwo { name: String, alignment: u32, backtrace: Backtrace },
42    #[snafu(display("Section {name} starts at a misaligned address {start_address:#010x}; the provided alignment was {alignment}:\n{backtrace}"))]
43    MisalignedStart { name: String, start_address: u32, alignment: u32, backtrace: Backtrace },
44}
45
46#[derive(Debug, Snafu)]
47pub enum SectionParseError {
48    #[snafu(transparent)]
49    SectionKind { source: SectionKindError },
50    #[snafu(display("{context}: failed to parse start address '{value}': {error}\n{backtrace}"))]
51    ParseStartAddress { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
52    #[snafu(display("{context}: failed to parse end address '{value}': {error}\n{backtrace}"))]
53    ParseEndAddress { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
54    #[snafu(display("{context}: failed to parse alignment '{value}': {error}\n{backtrace}"))]
55    ParseAlignment { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
56    #[snafu(display(
57        "{context}: expected section attribute 'kind', 'start', 'end' or 'align' but got '{key}':\n{backtrace}"
58    ))]
59    UnknownAttribute { context: ParseContext, key: String, backtrace: Backtrace },
60    #[snafu(display("{context}: missing '{attribute}' attribute:\n{backtrace}"))]
61    MissingAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
62    #[snafu(display("{context}: {error}"))]
63    Section { context: ParseContext, error: SectionError },
64}
65
66#[derive(Debug, Snafu)]
67pub enum SectionInheritParseError {
68    #[snafu(display("{context}: section {name} does not exist in this file's header:\n{backtrace}"))]
69    NotInHeader { context: ParseContext, name: String, backtrace: Backtrace },
70    #[snafu(display("{context}: attribute '{attribute}' should be omitted as it is inherited from this file's header"))]
71    InheritedAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
72    #[snafu(transparent)]
73    SectionParse { source: SectionParseError },
74}
75
76#[derive(Debug, Snafu)]
77pub enum SectionCodeError {
78    #[snafu(display("section starts before base address:\n{backtrace}"))]
79    StartsBeforeBaseAddress { backtrace: Backtrace },
80    #[snafu(display("section ends after code ends:\n{backtrace}"))]
81    EndsOutsideModule { backtrace: Backtrace },
82}
83
84pub struct SectionOptions {
85    pub name: String,
86    pub kind: SectionKind,
87    pub start_address: u32,
88    pub end_address: u32,
89    pub alignment: u32,
90    pub functions: Option<BTreeMap<u32, Function>>,
91}
92
93impl Section {
94    pub fn new(options: SectionOptions) -> Result<Self, SectionError> {
95        let SectionOptions { name, kind, start_address, end_address, alignment, functions } = options;
96
97        if end_address < start_address {
98            return EndBeforeStartSnafu { name, start_address, end_address }.fail();
99        }
100        if !alignment.is_power_of_two() {
101            return AlignmentPowerOfTwoSnafu { name, alignment }.fail();
102        }
103        let misalign_mask = alignment - 1;
104        if (start_address & misalign_mask) != 0 {
105            return MisalignedStartSnafu { name, start_address, alignment }.fail();
106        }
107
108        let functions = functions.unwrap_or_else(BTreeMap::new);
109
110        Ok(Self { name, kind, start_address, end_address, alignment, functions })
111    }
112
113    pub fn inherit(other: &Section, start_address: u32, end_address: u32) -> Result<Self, SectionError> {
114        if end_address < start_address {
115            return EndBeforeStartSnafu { name: other.name.clone(), start_address, end_address }.fail();
116        }
117        Ok(Self {
118            name: other.name.clone(),
119            kind: other.kind,
120            start_address,
121            end_address,
122            alignment: other.alignment,
123            functions: BTreeMap::new(),
124        })
125    }
126
127    pub(crate) fn parse(line: &str, context: &ParseContext) -> Result<Option<Self>, SectionParseError> {
128        let mut words = line.split_whitespace();
129        let Some(name) = words.next() else { return Ok(None) };
130
131        let mut kind = None;
132        let mut start = None;
133        let mut end = None;
134        let mut align = None;
135        for (key, value) in iter_attributes(words) {
136            match key {
137                "kind" => kind = Some(SectionKind::parse(value, context)?),
138                "start" => {
139                    start = Some(parse_u32(value).map_err(|error| ParseStartAddressSnafu { context, value, error }.build())?)
140                }
141                "end" => end = Some(parse_u32(value).map_err(|error| ParseEndAddressSnafu { context, value, error }.build())?),
142                "align" => {
143                    align = Some(parse_u32(value).map_err(|error| ParseAlignmentSnafu { context, value, error }.build())?)
144                }
145                _ => return UnknownAttributeSnafu { context: context.clone(), key }.fail(),
146            }
147        }
148
149        let kind = kind.ok_or_else(|| MissingAttributeSnafu { context, attribute: "kind" }.build())?;
150        let start_address = start.ok_or_else(|| MissingAttributeSnafu { context, attribute: "start" }.build())?;
151        let end_address = end.ok_or_else(|| MissingAttributeSnafu { context, attribute: "end" }.build())?;
152        let alignment = align.ok_or_else(|| MissingAttributeSnafu { context, attribute: "align" }.build())?;
153
154        Ok(Some(
155            Section::new(SectionOptions {
156                name: name.to_string(),
157                kind,
158                start_address,
159                end_address,
160                alignment,
161                functions: None,
162            })
163            .map_err(|error| SectionSnafu { context, error }.build())?,
164        ))
165    }
166
167    pub(crate) fn parse_inherit(
168        line: &str,
169        context: &ParseContext,
170        sections: &Sections,
171    ) -> Result<Option<Self>, SectionInheritParseError> {
172        let mut words = line.split_whitespace();
173        let Some(name) = words.next() else { return Ok(None) };
174
175        let inherit_section = if name != DTCM_SECTION {
176            Some(
177                sections
178                    .by_name(name)
179                    .map(|(_, section)| section)
180                    .ok_or_else(|| NotInHeaderSnafu { context, name }.build())?,
181            )
182        } else {
183            None
184        };
185
186        let mut start = None;
187        let mut end = None;
188        for (key, value) in iter_attributes(words) {
189            match key {
190                "kind" => return InheritedAttributeSnafu { context, attribute: "kind" }.fail(),
191                "start" => {
192                    start = Some(parse_u32(value).map_err(|error| ParseStartAddressSnafu { context, value, error }.build())?)
193                }
194                "end" => end = Some(parse_u32(value).map_err(|error| ParseEndAddressSnafu { context, value, error }.build())?),
195                "align" => return InheritedAttributeSnafu { context, attribute: "align" }.fail(),
196                _ => return UnknownAttributeSnafu { context, key }.fail()?,
197            }
198        }
199
200        let start = start.ok_or_else(|| MissingAttributeSnafu { context, attribute: "start" }.build())?;
201        let end = end.ok_or_else(|| MissingAttributeSnafu { context, attribute: "end" }.build())?;
202
203        if name == DTCM_SECTION {
204            Ok(Some(Section {
205                name: name.to_string(),
206                kind: SectionKind::Bss,
207                start_address: start,
208                end_address: end,
209                alignment: 4,
210                functions: BTreeMap::new(),
211            }))
212        } else {
213            let inherit_section = inherit_section.unwrap();
214            Ok(Some(Section::inherit(inherit_section, start, end).map_err(|error| SectionSnafu { context, error }.build())?))
215        }
216    }
217
218    pub fn code_from_module<'a>(&'a self, module: &'a Module) -> Result<Option<&'a [u8]>, SectionCodeError> {
219        self.code(module.code(), module.base_address())
220    }
221
222    pub fn code<'a>(&'a self, code: &'a [u8], base_address: u32) -> Result<Option<&'a [u8]>, SectionCodeError> {
223        if self.kind() == SectionKind::Bss {
224            return Ok(None);
225        }
226        if self.start_address() < base_address {
227            return StartsBeforeBaseAddressSnafu.fail();
228        }
229        let start = self.start_address() - base_address;
230        let end = self.end_address() - base_address;
231        if end > code.len() as u32 {
232            return EndsOutsideModuleSnafu.fail();
233        }
234        Ok(Some(&code[start as usize..end as usize]))
235    }
236
237    pub fn size(&self) -> u32 {
238        self.end_address - self.start_address
239    }
240
241    /// Iterates over every 32-bit word in the specified `range`, which defaults to the entire section if it is `None`. Note
242    /// that `code` must be the full raw content of this section.
243    pub fn iter_words<'a>(&'a self, code: &'a [u8], range: Option<Range<u32>>) -> impl Iterator<Item = Word> + 'a {
244        let range = range.unwrap_or(self.address_range());
245        let start = range.start.next_multiple_of(4);
246        let end = range.end & !3;
247
248        (start..end).step_by(4).map(move |address| {
249            let offset = address - self.start_address();
250            let bytes = &code[offset as usize..];
251            Word { address, value: u32::from_le_slice(bytes) }
252        })
253    }
254
255    pub fn name(&self) -> &str {
256        &self.name
257    }
258
259    pub fn kind(&self) -> SectionKind {
260        self.kind
261    }
262
263    pub fn start_address(&self) -> u32 {
264        self.start_address
265    }
266
267    pub fn end_address(&self) -> u32 {
268        self.end_address
269    }
270
271    pub fn address_range(&self) -> Range<u32> {
272        self.start_address..self.end_address
273    }
274
275    pub fn alignment(&self) -> u32 {
276        self.alignment
277    }
278
279    pub fn overlaps_with(&self, other: &Section) -> bool {
280        self.start_address < other.end_address && other.start_address < self.end_address
281    }
282
283    pub fn functions(&self) -> &BTreeMap<u32, Function> {
284        &self.functions
285    }
286
287    pub fn functions_mut(&mut self) -> &mut BTreeMap<u32, Function> {
288        &mut self.functions
289    }
290
291    pub fn add_function(&mut self, function: Function) {
292        self.functions.insert(function.start_address(), function);
293    }
294}
295
296impl Display for Section {
297    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298        write!(
299            f,
300            "{:11} start:{:#010x} end:{:#010x} kind:{} align:{}",
301            self.name, self.start_address, self.end_address, self.kind, self.alignment
302        )?;
303
304        Ok(())
305    }
306}
307
308#[derive(PartialEq, Eq, Clone, Copy, Serialize)]
309pub enum SectionKind {
310    Code,
311    Data,
312    Rodata,
313    Bss,
314    // /// Special section for adding .bss objects to the DTCM module
315    // Dtcm,
316}
317
318#[derive(Debug, Snafu)]
319pub enum SectionKindError {
320    #[snafu(display("{context}: unknown section kind '{value}', must be one of: code, data, bss"))]
321    UnknownKind { context: ParseContext, value: String, backtrace: Backtrace },
322}
323
324impl SectionKind {
325    pub fn parse(value: &str, context: &ParseContext) -> Result<Self, SectionKindError> {
326        match value {
327            "code" => Ok(Self::Code),
328            "data" => Ok(Self::Data),
329            "rodata" => Ok(Self::Rodata),
330            "bss" => Ok(Self::Bss),
331            _ => UnknownKindSnafu { context, value }.fail(),
332        }
333    }
334
335    pub fn is_initialized(self) -> bool {
336        match self {
337            SectionKind::Code => true,
338            SectionKind::Data => true,
339            SectionKind::Rodata => true,
340            SectionKind::Bss => false,
341        }
342    }
343
344    pub fn is_writeable(self) -> bool {
345        match self {
346            SectionKind::Code => false,
347            SectionKind::Data => true,
348            SectionKind::Rodata => false,
349            SectionKind::Bss => true,
350        }
351    }
352
353    pub fn is_executable(self) -> bool {
354        match self {
355            SectionKind::Code => true,
356            SectionKind::Data => false,
357            SectionKind::Rodata => false,
358            SectionKind::Bss => false,
359        }
360    }
361}
362
363impl Display for SectionKind {
364    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365        match self {
366            Self::Code => write!(f, "code"),
367            Self::Data => write!(f, "data"),
368            Self::Rodata => write!(f, "rodata"),
369            Self::Bss => write!(f, "bss"),
370        }
371    }
372}
373
374pub struct Sections {
375    sections: Vec<Section>,
376    sections_by_name: HashMap<String, SectionIndex>,
377}
378
379#[derive(Debug, Snafu)]
380pub enum SectionsError {
381    #[snafu(display("Section '{name}' already exists:\n{backtrace}"))]
382    DuplicateName { name: String, backtrace: Backtrace },
383    #[snafu(display("Section '{name}' overlaps with '{other_name}':\n{backtrace}"))]
384    Overlapping { name: String, other_name: String, backtrace: Backtrace },
385}
386
387impl Sections {
388    pub fn new() -> Self {
389        Self { sections: vec![], sections_by_name: HashMap::new() }
390    }
391
392    pub fn from_sections(section_vec: Vec<Section>) -> Result<Self, SectionsError> {
393        let mut sections = Self::new();
394        for section in section_vec {
395            sections.add(section)?;
396        }
397        Ok(sections)
398    }
399
400    pub fn add(&mut self, section: Section) -> Result<SectionIndex, SectionsError> {
401        if self.sections_by_name.contains_key(&section.name) {
402            return DuplicateNameSnafu { name: section.name }.fail();
403        }
404        for other in &self.sections {
405            if section.overlaps_with(other) {
406                return OverlappingSnafu { name: section.name, other_name: other.name.to_string() }.fail();
407            }
408        }
409
410        let index = SectionIndex(self.sections.len());
411        self.sections_by_name.insert(section.name.clone(), index);
412        self.sections.push(section);
413        Ok(index)
414    }
415
416    pub fn remove(&mut self, name: &str) {
417        let Some(index) = self.sections_by_name.remove(name) else {
418            return;
419        };
420        self.sections.remove(index.0);
421        // Update indices in sections_by_name
422        for (i, section) in self.sections.iter().enumerate() {
423            self.sections_by_name.insert(section.name.clone(), SectionIndex(i));
424        }
425    }
426
427    pub fn get(&self, index: SectionIndex) -> &Section {
428        &self.sections[index.0]
429    }
430
431    pub fn get_mut(&mut self, index: SectionIndex) -> &mut Section {
432        &mut self.sections[index.0]
433    }
434
435    pub fn by_name(&self, name: &str) -> Option<(SectionIndex, &Section)> {
436        let &index = self.sections_by_name.get(name)?;
437        Some((index, &self.sections[index.0]))
438    }
439
440    pub fn iter(&self) -> impl Iterator<Item = &Section> {
441        self.sections.iter()
442    }
443
444    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Section> {
445        self.sections.iter_mut()
446    }
447
448    pub fn len(&self) -> usize {
449        self.sections.len()
450    }
451
452    pub fn get_by_contained_address(&self, address: u32) -> Option<(SectionIndex, &Section)> {
453        self.sections
454            .iter()
455            .enumerate()
456            .find(|(_, s)| address >= s.start_address && address < s.end_address)
457            .map(|(i, s)| (SectionIndex(i), s))
458    }
459
460    pub fn get_by_contained_address_mut(&mut self, address: u32) -> Option<(SectionIndex, &mut Section)> {
461        self.sections
462            .iter_mut()
463            .enumerate()
464            .find(|(_, s)| address >= s.start_address && address < s.end_address)
465            .map(|(i, s)| (SectionIndex(i), s))
466    }
467
468    pub fn add_function(&mut self, function: Function) {
469        let address = function.first_instruction_address();
470        self.sections
471            .iter_mut()
472            .find(|s| address >= s.start_address && address < s.end_address)
473            .unwrap()
474            .functions
475            .insert(address, function);
476    }
477
478    pub fn sorted_by_address(&self) -> Vec<&Section> {
479        let mut sections = self.sections.iter().collect::<Vec<_>>();
480        sections.sort_unstable_by(|a, b| a.start_address.cmp(&b.start_address).then(a.end_address.cmp(&b.end_address)));
481        sections
482    }
483
484    pub fn functions(&self) -> impl Iterator<Item = &Function> {
485        self.sections.iter().flat_map(|s| s.functions.values())
486    }
487
488    pub fn functions_mut(&mut self) -> impl Iterator<Item = &mut Function> {
489        self.sections.iter_mut().flat_map(|s| s.functions.values_mut())
490    }
491
492    pub fn base_address(&self) -> Option<u32> {
493        self.sections.iter().map(|s| s.start_address).min()
494    }
495
496    pub fn end_address(&self) -> Option<u32> {
497        self.sections.iter().map(|s| s.end_address).max()
498    }
499
500    pub fn text_size(&self) -> u32 {
501        self.sections.iter().filter(|s| s.kind != SectionKind::Bss).map(|s| s.size()).sum()
502    }
503
504    pub fn bss_size(&self) -> u32 {
505        self.sections.iter().filter(|s| s.kind == SectionKind::Bss).map(|s| s.size()).sum()
506    }
507
508    pub fn bss_range(&self) -> Option<Range<u32>> {
509        self.sections
510            .iter()
511            .filter(|s| s.kind == SectionKind::Bss)
512            .map(|s| s.address_range())
513            .reduce(|a, b| a.start.min(b.start)..a.end.max(b.end))
514    }
515
516    pub fn get_section_after(&self, text_end: u32) -> Option<&Section> {
517        self.sorted_by_address().iter().copied().find(|s| s.start_address >= text_end)
518    }
519}
520
521impl IntoIterator for Sections {
522    type Item = Section;
523
524    type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
525
526    fn into_iter(self) -> Self::IntoIter {
527        self.sections.into_iter()
528    }
529}
530
531pub struct Word {
532    pub address: u32,
533    pub value: u32,
534}