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