Skip to main content

ds_decomp/config/
module.rs

1use std::{
2    backtrace::Backtrace,
3    collections::{BTreeMap, BTreeSet},
4    fmt::Display,
5};
6
7use ds_rom::rom::{
8    Arm9, Autoload, Overlay,
9    raw::{AutoloadKind, RawBuildInfoError},
10};
11use snafu::Snafu;
12
13use self::data::FindLocalDataError;
14use super::{
15    relocations::Relocations,
16    section::{
17        Section, SectionCodeError, SectionError, SectionKind, SectionOptions, Sections,
18        SectionsError,
19    },
20    symbol::{SymData, SymbolKind, SymbolMap, SymbolMapError, SymbolMaps},
21};
22use crate::{
23    analysis::{
24        ctor::{CtorRange, CtorRangeError},
25        data::{self, FindLocalDataOptions},
26        exception::{ExceptionData, ExceptionDataError},
27        functions::{
28            FindFunctionsOptions, Function, FunctionAnalysisError, FunctionParseOptions,
29            FunctionSearchOptions, IntoFunctionError, ParseFunctionError, ParseFunctionOptions,
30        },
31        main::{MainFunction, MainFunctionError},
32    },
33    config::{
34        Comments, link_time_const::LinkTimeConst, relocations::RelocationKind, symbol::Symbol,
35    },
36};
37
38pub struct Module {
39    name: String,
40    kind: ModuleKind,
41    relocations: Relocations,
42    code: Vec<u8>,
43    base_address: u32,
44    bss_size: u32,
45    pub default_func_prefix: String,
46    pub default_data_prefix: String,
47    pub default_sinit_prefix: String,
48    sections: Sections,
49    signed: bool,
50}
51
52#[derive(Debug, Snafu)]
53pub enum ModuleError {
54    #[snafu(display("no sections provided:\n{backtrace}"))]
55    NoSections { backtrace: Backtrace },
56    #[snafu(transparent)]
57    CtorRange { source: CtorRangeError },
58    #[snafu(transparent)]
59    MainFunction { source: MainFunctionError },
60    #[snafu(transparent)]
61    RawBuildInfo { source: RawBuildInfoError },
62    #[snafu(transparent)]
63    SymbolMap { source: SymbolMapError },
64    #[snafu(transparent)]
65    FunctionAnalysis { source: FunctionAnalysisError },
66    #[snafu(display("function {name} could not be analyzed: {parse_result:x?}:\n{backtrace}"))]
67    FunctionAnalysisFailed { name: String, parse_result: ParseFunctionError, backtrace: Backtrace },
68    #[snafu(transparent)]
69    Section { source: SectionError },
70    #[snafu(transparent)]
71    Sections { source: SectionsError },
72    #[snafu(display(
73        ".init section exists in {module_kind} ({min_address:#x}..{max_address:#x}) but no functions were found:\n{backtrace}"
74    ))]
75    NoInitFunctions {
76        module_kind: ModuleKind,
77        min_address: u32,
78        max_address: u32,
79        backtrace: Backtrace,
80    },
81    #[snafu(display("Entry functions not found:\n{backtrace}"))]
82    NoEntryFunctions { backtrace: Backtrace },
83    #[snafu(display("No functions in ARM9 main module:\n{backtrace}"))]
84    NoArm9Functions { backtrace: Backtrace },
85    #[snafu(display("No functions in ITCM:\n{backtrace}"))]
86    NoItcmFunctions { backtrace: Backtrace },
87    #[snafu(transparent)]
88    FindLocalData { source: FindLocalDataError },
89    #[snafu(transparent)]
90    SectionCode { source: SectionCodeError },
91    #[snafu(display("The provided autoload is not an unknown autoload:\n{backtrace}"))]
92    NotAnUnknownAutoload { backtrace: Backtrace },
93    #[snafu(transparent)]
94    ExceptionData { source: ExceptionDataError },
95}
96
97pub struct OverlayModuleOptions<'a> {
98    pub id: u16,
99    pub code: &'a [u8],
100    pub signed: bool,
101}
102
103pub struct ModuleOptions<'a> {
104    pub kind: ModuleKind,
105    pub name: String,
106    pub relocations: Relocations,
107    pub sections: Sections,
108    pub code: &'a [u8],
109    pub signed: bool,
110}
111
112impl Module {
113    pub fn new(symbol_map: &mut SymbolMap, options: ModuleOptions) -> Result<Module, ModuleError> {
114        let ModuleOptions { kind, name, relocations, mut sections, code, signed } = options;
115
116        let base_address = sections.base_address().ok_or_else(|| NoSectionsSnafu.build())?;
117        let end_address = sections.end_address().ok_or_else(|| NoSectionsSnafu.build())?;
118        let bss_size = sections.bss_size();
119        Self::import_functions(symbol_map, &mut sections, base_address, end_address, code)?;
120
121        let (default_func_prefix, default_data_prefix, default_sinit_prefix) = match kind {
122            ModuleKind::Overlay(id) => (
123                format!("func_ov{id:03}_"),
124                format!("data_ov{id:03}_"),
125                format!("__sinit_ov{id:03}_"),
126            ),
127            _ => ("func_".to_string(), "data_".to_string(), "__sinit_".to_string()),
128        };
129
130        Ok(Self {
131            name,
132            kind,
133            relocations,
134            code: code.to_vec(),
135            base_address,
136            bss_size,
137            default_func_prefix,
138            default_data_prefix,
139            default_sinit_prefix,
140            sections,
141            signed,
142        })
143    }
144
145    /// Depricated, use [`Self::new`] instead.
146    ///
147    /// Creates a new ARM9 main module.
148    #[deprecated]
149    pub fn new_arm9(
150        name: String,
151        symbol_map: &mut SymbolMap,
152        relocations: Relocations,
153        mut sections: Sections,
154        code: &[u8],
155    ) -> Result<Module, ModuleError> {
156        let base_address = sections.base_address().ok_or_else(|| NoSectionsSnafu.build())?;
157        let end_address = sections.end_address().ok_or_else(|| NoSectionsSnafu.build())?;
158        let bss_size = sections.bss_size();
159        Self::import_functions(symbol_map, &mut sections, base_address, end_address, code)?;
160        Ok(Self {
161            name,
162            kind: ModuleKind::Arm9,
163            relocations,
164            code: code.to_vec(),
165            base_address,
166            bss_size,
167            default_func_prefix: "func_".to_string(),
168            default_data_prefix: "data_".to_string(),
169            default_sinit_prefix: "__sinit_".to_string(),
170            sections,
171            signed: false,
172        })
173    }
174
175    pub fn analyze_arm9(
176        arm9: &Arm9,
177        unknown_autoloads: &[&Autoload],
178        symbol_maps: &mut SymbolMaps,
179        options: &AnalysisOptions,
180    ) -> Result<Self, ModuleError> {
181        let ctor_range = CtorRange::find_in_arm9(arm9, unknown_autoloads)?;
182        let main_func = MainFunction::find_in_arm9(arm9)?;
183        let exception_data = ExceptionData::analyze(arm9, unknown_autoloads)?;
184
185        let mut module = Self {
186            name: "main".to_string(),
187            kind: ModuleKind::Arm9,
188            relocations: Relocations::new(),
189            code: arm9.code()?.to_vec(),
190            base_address: arm9.base_address(),
191            bss_size: arm9.bss()?.len() as u32,
192            default_func_prefix: "func_".to_string(),
193            default_data_prefix: "data_".to_string(),
194            default_sinit_prefix: "__sinit_".to_string(),
195            sections: Sections::new(),
196            signed: false,
197        };
198        let symbol_map = symbol_maps.get_mut(module.kind);
199
200        module.find_sections_arm9(symbol_map, &ctor_range, exception_data, arm9)?;
201        module.find_data_from_pools(
202            symbol_map,
203            options,
204            Some(BTreeMap::from([
205                // Empty .ctor sections won't be detected by relocation analysis, so instead
206                // override any pointer to .ctor to a link-time constant relocation
207                (ctor_range.start, RelocationKind::LinkTimeConst(LinkTimeConst::Arm9CtorStart)),
208            ])),
209        )?;
210        module.find_data_from_sections(symbol_map, options)?;
211
212        symbol_map.rename_by_address(arm9.entry_function(), "Entry")?;
213        symbol_map.rename_by_address(main_func.address, "main")?;
214
215        Ok(module)
216    }
217
218    /// Depricated, use [`Self::new`] instead.
219    ///
220    /// Creates a new overlay module.
221    #[deprecated]
222    pub fn new_overlay(
223        name: String,
224        symbol_map: &mut SymbolMap,
225        relocations: Relocations,
226        mut sections: Sections,
227        options: OverlayModuleOptions,
228    ) -> Result<Self, ModuleError> {
229        let OverlayModuleOptions { id, code, signed } = options;
230
231        let base_address = sections.base_address().ok_or_else(|| NoSectionsSnafu.build())?;
232        let end_address = sections.end_address().ok_or_else(|| NoSectionsSnafu.build())?;
233        let bss_size = sections.bss_size();
234        Self::import_functions(symbol_map, &mut sections, base_address, end_address, code)?;
235        Ok(Self {
236            name,
237            kind: ModuleKind::Overlay(id),
238            relocations,
239            code: code.to_vec(),
240            base_address,
241            bss_size,
242            default_func_prefix: format!("func_ov{id:03}_"),
243            default_data_prefix: format!("data_ov{id:03}_"),
244            default_sinit_prefix: format!("__sinit_ov{id:03}_"),
245            sections,
246            signed,
247        })
248    }
249
250    pub fn analyze_overlay(
251        overlay: &Overlay,
252        symbol_maps: &mut SymbolMaps,
253        options: &AnalysisOptions,
254    ) -> Result<Self, ModuleError> {
255        let mut module = Self {
256            name: format!("ov{:03}", overlay.id()),
257            kind: ModuleKind::Overlay(overlay.id()),
258            relocations: Relocations::new(),
259            code: overlay.code().to_vec(),
260            base_address: overlay.base_address(),
261            bss_size: overlay.bss_size(),
262            default_func_prefix: format!("func_ov{:03}_", overlay.id()),
263            default_data_prefix: format!("data_ov{:03}_", overlay.id()),
264            default_sinit_prefix: format!("__sinit_ov{:03}_", overlay.id()),
265            sections: Sections::new(),
266            signed: overlay.is_signed(),
267        };
268        let symbol_map = symbol_maps.get_mut(module.kind);
269
270        log::debug!("Analyzing overlay {}", overlay.id());
271        module.find_sections_overlay(symbol_map, CtorRange {
272            start: overlay.ctor_start(),
273            end: overlay.ctor_end(),
274        })?;
275        module.find_data_from_pools(symbol_map, options, None)?;
276        module.find_data_from_sections(symbol_map, options)?;
277
278        Ok(module)
279    }
280
281    /// Depricated, use [`Self::new`] instead.
282    ///
283    /// Creates a new autoload module.
284    #[deprecated]
285    pub fn new_autoload(
286        name: String,
287        symbol_map: &mut SymbolMap,
288        relocations: Relocations,
289        mut sections: Sections,
290        kind: AutoloadKind,
291        code: &[u8],
292    ) -> Result<Self, ModuleError> {
293        let base_address = sections.base_address().ok_or_else(|| NoSectionsSnafu.build())?;
294        let end_address = sections.end_address().ok_or_else(|| NoSectionsSnafu.build())?;
295        let bss_size = sections.bss_size();
296        Self::import_functions(symbol_map, &mut sections, base_address, end_address, code)?;
297        Ok(Self {
298            name,
299            kind: ModuleKind::Autoload(kind),
300            relocations,
301            code: code.to_vec(),
302            base_address,
303            bss_size,
304            default_func_prefix: "func_".to_string(),
305            default_data_prefix: "data_".to_string(),
306            default_sinit_prefix: "__sinit_".to_string(),
307            sections,
308            signed: false,
309        })
310    }
311
312    pub fn analyze_itcm(
313        autoload: &Autoload,
314        symbol_maps: &mut SymbolMaps,
315        options: &AnalysisOptions,
316    ) -> Result<Self, ModuleError> {
317        let mut module = Self {
318            name: "itcm".to_string(),
319            kind: ModuleKind::Autoload(AutoloadKind::Itcm),
320            relocations: Relocations::new(),
321            code: autoload.code().to_vec(),
322            base_address: autoload.base_address(),
323            bss_size: autoload.bss_size(),
324            default_func_prefix: "func_".to_string(),
325            default_data_prefix: "data_".to_string(),
326            default_sinit_prefix: "__sinit_".to_string(),
327            sections: Sections::new(),
328            signed: false,
329        };
330        let symbol_map = symbol_maps.get_mut(module.kind);
331
332        module.find_sections_itcm(symbol_map)?;
333        module.find_data_from_pools(symbol_map, options, None)?;
334
335        Ok(module)
336    }
337
338    pub fn analyze_dtcm(
339        autoload: &Autoload,
340        symbol_maps: &mut SymbolMaps,
341        options: &AnalysisOptions,
342    ) -> Result<Self, ModuleError> {
343        let mut module = Self {
344            name: "dtcm".to_string(),
345            kind: ModuleKind::Autoload(AutoloadKind::Dtcm),
346            relocations: Relocations::new(),
347            code: autoload.code().to_vec(),
348            base_address: autoload.base_address(),
349            bss_size: autoload.bss_size(),
350            default_func_prefix: "func_".to_string(),
351            default_data_prefix: "data_".to_string(),
352            default_sinit_prefix: "__sinit_".to_string(),
353            sections: Sections::new(),
354            signed: false,
355        };
356        let symbol_map = symbol_maps.get_mut(module.kind);
357
358        module.find_sections_dtcm()?;
359        module.find_data_from_sections(symbol_map, options)?;
360
361        Ok(module)
362    }
363
364    pub fn analyze_unknown_autoload(
365        autoload: &Autoload,
366        symbol_maps: &mut SymbolMaps,
367        options: &AnalysisOptions,
368    ) -> Result<Self, ModuleError> {
369        let AutoloadKind::Unknown(autoload_index) = autoload.kind() else {
370            return NotAnUnknownAutoloadSnafu.fail();
371        };
372        let mut module = Self {
373            name: format!("autoload_{autoload_index}"),
374            kind: ModuleKind::Autoload(autoload.kind()),
375            relocations: Relocations::new(),
376            code: autoload.code().to_vec(),
377            base_address: autoload.base_address(),
378            bss_size: autoload.bss_size(),
379            default_func_prefix: "func_".to_string(),
380            default_data_prefix: "data_".to_string(),
381            default_sinit_prefix: "__sinit_".to_string(),
382            sections: Sections::new(),
383            signed: false,
384        };
385        let symbol_map = symbol_maps.get_mut(module.kind);
386
387        module.find_sections_unknown_autoload(symbol_map, autoload)?;
388        module.find_data_from_pools(symbol_maps.get_mut(module.kind), options, None)?;
389        module.find_data_from_sections(symbol_maps.get_mut(module.kind), options)?;
390
391        Ok(module)
392    }
393
394    fn import_functions(
395        symbol_map: &mut SymbolMap,
396        sections: &mut Sections,
397        base_address: u32,
398        end_address: u32,
399        code: &[u8],
400    ) -> Result<(), ModuleError> {
401        for (sym_function, symbol) in symbol_map.clone_functions() {
402            if sym_function.unknown {
403                continue;
404            }
405            let offset = symbol.addr - base_address;
406            let size = sym_function.size;
407            let parse_result = Function::parse_function(FunctionParseOptions {
408                name: symbol.name.clone(),
409                start_address: symbol.addr,
410                base_address: symbol.addr,
411                module_code: &code[offset as usize..],
412                known_end_address: Some(symbol.addr + size),
413                module_start_address: base_address,
414                module_end_address: end_address,
415                parse_options: ParseFunctionOptions { thumb: sym_function.mode.into_thumb() },
416                ..Default::default()
417            });
418            let function = match parse_result {
419                Ok(function) => function,
420                Err(FunctionAnalysisError::IntoFunction {
421                    source: IntoFunctionError::ParseFunction { source },
422                }) => {
423                    return FunctionAnalysisFailedSnafu { name: symbol.name, parse_result: source }
424                        .fail();
425                }
426                Err(e) => return Err(e.into()),
427            };
428            function.add_local_symbols_to_map(symbol_map)?;
429            sections.add_function(function);
430        }
431        Ok(())
432    }
433
434    fn find_functions(
435        &mut self,
436        symbol_map: &mut SymbolMap,
437        search_options: FunctionSearchOptions,
438        func_prefix: &str,
439    ) -> Result<Option<FoundFunctions>, ModuleError> {
440        let functions = Function::find_functions(FindFunctionsOptions {
441            default_name_prefix: func_prefix,
442            base_address: self.base_address,
443            module_code: &self.code,
444            symbol_map,
445            module_start_address: self.base_address,
446            module_end_address: self.end_address(),
447            search_options,
448        })?;
449
450        if functions.is_empty() {
451            Ok(None)
452        } else {
453            let start = functions.first_key_value().unwrap().1.start_address();
454            // Align by 4 in case of Thumb function ending on a 2-byte boundary
455            let end = functions.last_key_value().unwrap().1.end_address().next_multiple_of(4);
456            log::debug!(
457                "Found {} functions in {}: {:#x} to {:#x}",
458                functions.len(),
459                self.kind,
460                start,
461                end
462            );
463            Ok(Some(FoundFunctions { functions, start, end }))
464        }
465    }
466
467    /// Adds the .ctor section to this module. Returns the min and max address of .init functions in the .ctor section.
468    fn add_ctor_section(
469        &mut self,
470        ctor_range: &CtorRange,
471        symbol_map: &mut SymbolMap,
472    ) -> Result<Option<InitFunctions>, ModuleError> {
473        // Every .ctor section ends with a zero written by the linker, so we subtract the end
474        // address by 4 to prevent users from including the final zero in their delinked files.
475        // This may cause the .ctor section to be empty, but it should not be omitted from the
476        // module, as that would also omit the WRITEW(0); instruction from the LCF.
477        let end_address = ctor_range.end - 4;
478        let section = Section::new(SectionOptions {
479            name: ".ctor".to_string(),
480            kind: SectionKind::Rodata,
481            start_address: ctor_range.start,
482            end_address,
483            alignment: 4,
484            functions: None,
485            comments: Comments::new(),
486        })?;
487        self.sections.add(section)?;
488
489        let start = (ctor_range.start - self.base_address) as usize;
490        let end = (ctor_range.end - self.base_address) as usize;
491        let ctor = &self.code[start..end];
492
493        let mut init_functions = InitFunctions(BTreeSet::new());
494
495        let mut prev_address = 0;
496        for (i, address) in ctor
497            .chunks(4)
498            .map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
499            .take_while(|&addr| addr != 0)
500            .enumerate()
501        {
502            if address >= prev_address {
503                prev_address = address;
504                init_functions.0.insert(address & !1);
505            } else {
506                // Not in order, abort
507
508                // TODO: Create other sections for initializer functions that are not in order in .ctor. As in, every subrange
509                // of functions that are in order gets is own section, so that .ctor can be delinked and linked in a correct
510                // order.
511            }
512
513            let symbol_address = ctor_range.start + i as u32 * 4;
514            symbol_map.add(Symbol::new_data(
515                format!(".p{}{:08x}", self.default_sinit_prefix, address & !1),
516                symbol_address,
517                SymData::Word { count: Some(1) },
518                false,
519            ));
520        }
521
522        if init_functions.0.is_empty() {
523            Ok(None)
524        } else {
525            Ok(Some(init_functions))
526        }
527    }
528
529    /// Adds the .init section to this module. Returns the start and end address of the .init section.
530    fn add_init_section(
531        &mut self,
532        symbol_map: &mut SymbolMap,
533        ctor: &CtorRange,
534        init_functions: InitFunctions,
535        continuous: bool,
536    ) -> Result<Option<(u32, u32)>, ModuleError> {
537        let functions_min = *init_functions.0.first().unwrap();
538        let functions_max = *init_functions.0.last().unwrap();
539        let FoundFunctions { functions: init_functions, start: init_start, end: init_end } = self
540            .find_functions(
541                symbol_map,
542                FunctionSearchOptions {
543                    start_address: Some(functions_min),
544                    last_function_address: Some(functions_max),
545                    function_addresses: Some(init_functions.0),
546                    check_defs_uses: true,
547                    ..Default::default()
548                },
549                &self.default_sinit_prefix.clone(),
550            )?
551            .ok_or_else(|| {
552                NoInitFunctionsSnafu {
553                    module_kind: self.kind,
554                    min_address: functions_min,
555                    max_address: functions_max,
556                }
557                .build()
558            })?;
559        // Functions in .ctor can sometimes point to .text instead of .init
560        if !continuous || init_end == ctor.start {
561            self.sections.add(Section::new(SectionOptions {
562                name: ".init".to_string(),
563                kind: SectionKind::Code,
564                start_address: init_start,
565                end_address: init_end,
566                alignment: 4,
567                functions: Some(init_functions),
568                comments: Comments::new(),
569            })?)?;
570            Ok(Some((init_start, init_end)))
571        } else {
572            Ok(None)
573        }
574    }
575
576    /// Adds the .text section to this module.
577    fn add_text_section(&mut self, functions_result: FoundFunctions) -> Result<(), ModuleError> {
578        let FoundFunctions { functions, start, end } = functions_result;
579
580        if start < end {
581            self.sections.add(Section::new(SectionOptions {
582                name: ".text".to_string(),
583                kind: SectionKind::Code,
584                start_address: start,
585                end_address: end,
586                alignment: 32,
587                functions: Some(functions),
588                comments: Comments::new(),
589            })?)?;
590        }
591        Ok(())
592    }
593
594    fn add_rodata_section(&mut self, start: u32, end: u32) -> Result<(), ModuleError> {
595        if start < end {
596            self.sections.add(Section::new(SectionOptions {
597                name: ".rodata".to_string(),
598                kind: SectionKind::Rodata,
599                start_address: start,
600                end_address: end,
601                alignment: 4,
602                functions: None,
603                comments: Comments::new(),
604            })?)?;
605        }
606        Ok(())
607    }
608
609    fn add_data_section(&mut self, start: u32, end: u32) -> Result<(), ModuleError> {
610        if start < end {
611            self.sections.add(Section::new(SectionOptions {
612                name: ".data".to_string(),
613                kind: SectionKind::Data,
614                start_address: start,
615                end_address: end,
616                alignment: 32,
617                functions: None,
618                comments: Comments::new(),
619            })?)?;
620        }
621        Ok(())
622    }
623
624    fn add_bss_section(&mut self, start: u32) -> Result<(), ModuleError> {
625        self.sections.add(Section::new(SectionOptions {
626            name: ".bss".to_string(),
627            kind: SectionKind::Bss,
628            start_address: start,
629            end_address: start + self.bss_size,
630            alignment: 32,
631            functions: None,
632            comments: Comments::new(),
633        })?)?;
634        Ok(())
635    }
636
637    fn find_sections_overlay(
638        &mut self,
639        symbol_map: &mut SymbolMap,
640        ctor: CtorRange,
641    ) -> Result<(), ModuleError> {
642        let rodata_end = if let Some(init_functions) = self.add_ctor_section(&ctor, symbol_map)? {
643            if let Some((init_start, _)) =
644                self.add_init_section(symbol_map, &ctor, init_functions, true)?
645            {
646                init_start
647            } else {
648                ctor.start
649            }
650        } else {
651            ctor.start
652        };
653
654        let rodata_start = if let Some(functions_result) = self.find_functions(
655            symbol_map,
656            FunctionSearchOptions {
657                end_address: Some(rodata_end),
658                use_data_as_upper_bound: true,
659                check_defs_uses: true,
660                ..Default::default()
661            },
662            &self.default_func_prefix.clone(),
663        )? {
664            let end = functions_result.end;
665            self.add_text_section(functions_result)?;
666            end
667        } else {
668            self.base_address
669        };
670
671        self.add_rodata_section(rodata_start, rodata_end)?;
672
673        let data_start = ctor.end.next_multiple_of(32);
674        let data_end = self.base_address + self.code.len() as u32;
675        self.add_data_section(data_start, data_end)?;
676        self.add_bss_section(data_end)?;
677
678        Ok(())
679    }
680
681    fn find_sections_arm9(
682        &mut self,
683        symbol_map: &mut SymbolMap,
684        ctor: &CtorRange,
685        exception_data: Option<ExceptionData>,
686        arm9: &Arm9,
687    ) -> Result<(), ModuleError> {
688        // .ctor and .init
689        let (read_only_end, rodata_start) =
690            if let Some(init_functions) = self.add_ctor_section(ctor, symbol_map)? {
691                if let Some(init_range) =
692                    self.add_init_section(symbol_map, ctor, init_functions, false)?
693                {
694                    (init_range.0, Some(init_range.1))
695                } else {
696                    (ctor.start, None)
697                }
698            } else {
699                (ctor.start, None)
700            };
701
702        // Secure area functions (software interrupts)
703        let secure_area = &self.code[..0x800];
704        let mut functions =
705            Function::find_secure_area_functions(secure_area, self.base_address, symbol_map);
706
707        // Build info
708        let build_info_offset = arm9.build_info_offset();
709        let build_info_address = arm9.base_address() + build_info_offset;
710        symbol_map.add_data(Some("BuildInfo".to_string()), build_info_address, SymData::Any)?;
711
712        // Autoload callback
713        let autoload_callback_address = arm9.autoload_callback();
714        let name = "AutoloadCallback";
715        let parse_result = Function::parse_function(FunctionParseOptions {
716            name: name.to_string(),
717            start_address: autoload_callback_address,
718            base_address: self.base_address,
719            module_code: &self.code,
720            known_end_address: None,
721            module_start_address: self.base_address,
722            module_end_address: self.end_address(),
723            parse_options: ParseFunctionOptions::default(),
724            check_defs_uses: true,
725            existing_functions: Some(&functions),
726        });
727        let autoload_function = match parse_result {
728            Ok(function) => function,
729            Err(FunctionAnalysisError::IntoFunction {
730                source: IntoFunctionError::ParseFunction { source },
731            }) => {
732                return FunctionAnalysisFailedSnafu { name, parse_result: source }.fail();
733            }
734            Err(e) => return Err(e.into()),
735        };
736        symbol_map.add_function(&autoload_function);
737        functions.insert(autoload_function.first_instruction_address(), autoload_function);
738
739        // Entry functions
740        let FoundFunctions { functions: entry_functions, .. } = self
741            .find_functions(
742                symbol_map,
743                FunctionSearchOptions {
744                    start_address: Some(self.base_address + 0x800),
745                    end_address: Some(build_info_address),
746                    existing_functions: Some(&functions),
747                    check_defs_uses: true,
748                    ..Default::default()
749                },
750                &self.default_func_prefix.clone(),
751            )?
752            .ok_or_else(|| NoEntryFunctionsSnafu.build())?;
753        functions.extend(entry_functions);
754
755        // All other functions, starting from main
756        let exception_start = exception_data.as_ref().and_then(ExceptionData::exception_start);
757        let text_max = exception_start.unwrap_or(read_only_end);
758        let main_start = self.find_build_info_end_address(arm9);
759        let FoundFunctions { functions: text_functions, end: mut text_end, .. } = self
760            .find_functions(
761                symbol_map,
762                FunctionSearchOptions {
763                    start_address: Some(main_start),
764                    end_address: Some(text_max),
765                    // Skips over segments of strange EOR instructions which are never executed
766                    max_function_start_search_distance: u32::MAX,
767                    use_data_as_upper_bound: true,
768                    // There are some handwritten assembly functions in ARM9 main that don't follow the procedure call standard
769                    check_defs_uses: false,
770                    ..Default::default()
771                },
772                &self.default_func_prefix.clone(),
773            )?
774            .ok_or_else(|| NoArm9FunctionsSnafu.build())?;
775        let text_start = self.base_address;
776        functions.extend(text_functions);
777        self.add_text_section(FoundFunctions { functions, start: text_start, end: text_end })?;
778
779        // Add .exception and .exceptix sections if they exist
780        if let Some(exception_data) = exception_data {
781            if let Some(exception_start) = exception_data.exception_start() {
782                self.sections.add(Section::new(SectionOptions {
783                    name: ".exception".to_string(),
784                    kind: SectionKind::Rodata,
785                    start_address: exception_start,
786                    end_address: exception_data.exceptix_start(),
787                    alignment: 1,
788                    functions: None,
789                    comments: Comments::new(),
790                })?)?;
791            }
792
793            self.sections.add(Section::new(SectionOptions {
794                name: ".exceptix".to_string(),
795                kind: SectionKind::Rodata,
796                start_address: exception_data.exceptix_start(),
797                end_address: exception_data.exceptix_end(),
798                alignment: 4,
799                functions: None,
800                comments: Comments::new(),
801            })?)?;
802
803            text_end = exception_data.exceptix_end();
804        }
805
806        // .rodata
807        let rodata_start = rodata_start.unwrap_or(text_end);
808        self.add_rodata_section(rodata_start, ctor.start)?;
809
810        // .data and .bss
811        let data_start = ctor.end.next_multiple_of(32);
812        let data_end = self.base_address + self.code.len() as u32;
813        self.add_data_section(data_start, data_end)?;
814        let bss_start = data_end.next_multiple_of(32);
815        self.add_bss_section(bss_start)?;
816
817        let section_after_text = self.sections.get_section_after(text_end);
818        if let Some(section_after_text) = section_after_text
819            && text_end != section_after_text.start_address()
820        {
821            log::warn!(
822                "Expected .text to end ({:#010x}) where {} starts ({:#010x})",
823                text_end,
824                section_after_text.name(),
825                section_after_text.start_address()
826            );
827        }
828
829        Ok(())
830    }
831
832    fn find_build_info_end_address(&self, arm9: &Arm9) -> u32 {
833        let build_info_offset = arm9.build_info_offset();
834        let library_list_start = build_info_offset + 0x24; // 0x24 is the size of the build info struct
835
836        let mut offset = library_list_start as usize;
837        loop {
838            // Up to 4 bytes of zeros for alignment
839            let Some((library_offset, ch)) =
840                self.code[offset..offset + 4].iter().enumerate().find(|&(_, &b)| b != b'0')
841            else {
842                break;
843            };
844            if *ch != b'[' {
845                // Not a library name
846                break;
847            }
848            offset += library_offset;
849
850            let library_length = self.code[offset..].iter().position(|&b| b == b']').unwrap() + 1;
851            offset += library_length + 1; // +1 for the null terminator
852        }
853
854        arm9.base_address() + offset.next_multiple_of(4) as u32
855    }
856
857    fn find_sections_itcm(&mut self, symbol_map: &mut SymbolMap) -> Result<(), ModuleError> {
858        let text_functions = self
859            .find_functions(
860                symbol_map,
861                FunctionSearchOptions {
862                    // ITCM only contains code, so there's no risk of running into non-code by skipping illegal instructions
863                    max_function_start_search_distance: u32::MAX,
864                    // There are some handwritten assembly functions in the ITCM that don't follow the procedure call standard
865                    check_defs_uses: false,
866                    ..Default::default()
867                },
868                &self.default_func_prefix.clone(),
869            )?
870            .ok_or_else(|| NoItcmFunctionsSnafu.build())?;
871        let text_end = text_functions.end;
872        self.add_text_section(text_functions)?;
873
874        let bss_start = text_end.next_multiple_of(32);
875        self.add_bss_section(bss_start)?;
876
877        Ok(())
878    }
879
880    fn find_sections_dtcm(&mut self) -> Result<(), ModuleError> {
881        let data_start = self.base_address;
882        let data_end = data_start + self.code.len() as u32;
883        self.add_data_section(data_start, data_end)?;
884
885        let bss_start = data_end.next_multiple_of(32);
886        self.add_bss_section(bss_start)?;
887
888        Ok(())
889    }
890
891    fn find_sections_unknown_autoload(
892        &mut self,
893        symbol_map: &mut SymbolMap,
894        autoload: &Autoload,
895    ) -> Result<(), ModuleError> {
896        let base_address = autoload.base_address();
897        let AutoloadKind::Unknown(autoload_index) = autoload.kind() else {
898            panic!("Not an unknown autoload: {}", autoload.kind());
899        };
900        let code = autoload.code();
901
902        let text_functions = self.find_functions(
903            symbol_map,
904            FunctionSearchOptions {
905                max_function_start_search_distance: 32,
906                use_data_as_upper_bound: true,
907                // There are some handwritten assembly functions in unknown autoloads that don't follow the procedure call standard
908                check_defs_uses: false,
909                ..Default::default()
910            },
911            &self.default_func_prefix.clone(),
912        )?;
913
914        let text_end = if let Some(text_functions) = text_functions {
915            let text_end = text_functions.end;
916            self.add_text_section(text_functions)?;
917            text_end
918        } else {
919            self.base_address
920        };
921
922        let rodata_start = text_end.next_multiple_of(4);
923        let rodata_end = rodata_start.next_multiple_of(32);
924        log::warn!(
925            "Cannot determine size of .rodata in unknown autoload {autoload_index}, using {rodata_start:#010x}..{rodata_end:#010x}",
926        );
927        self.add_rodata_section(rodata_start, rodata_end)?;
928
929        let data_start = rodata_end;
930        let data_end = base_address + code.len() as u32;
931        self.add_data_section(data_start, data_end)?;
932
933        let bss_start = data_end.next_multiple_of(32);
934        self.add_bss_section(bss_start)?;
935
936        Ok(())
937    }
938
939    fn find_data_from_pools(
940        &mut self,
941        symbol_map: &mut SymbolMap,
942        options: &AnalysisOptions,
943        relocation_overrides: Option<BTreeMap<u32, RelocationKind>>,
944    ) -> Result<(), ModuleError> {
945        let relocation_overrides = relocation_overrides.unwrap_or_default();
946
947        for function in self.sections.functions() {
948            data::find_local_data_from_pools(
949                function,
950                FindLocalDataOptions {
951                    sections: &self.sections,
952                    module_kind: self.kind,
953                    symbol_map,
954                    relocations: &mut self.relocations,
955                    name_prefix: &self.default_data_prefix,
956                    code: &self.code,
957                    base_address: self.base_address,
958                    address_range: None,
959                    relocation_overrides: &relocation_overrides,
960                },
961                options,
962            )?;
963        }
964        Ok(())
965    }
966
967    fn find_data_from_sections(
968        &mut self,
969        symbol_map: &mut SymbolMap,
970        options: &AnalysisOptions,
971    ) -> Result<(), ModuleError> {
972        for section in self.sections.iter() {
973            match section.kind() {
974                SectionKind::Data | SectionKind::Rodata => {
975                    let code = section.code(&self.code, self.base_address)?.unwrap();
976                    data::find_local_data_from_section(
977                        section,
978                        FindLocalDataOptions {
979                            sections: &self.sections,
980                            module_kind: self.kind,
981                            symbol_map,
982                            relocations: &mut self.relocations,
983                            name_prefix: &self.default_data_prefix,
984                            code,
985                            base_address: self.base_address,
986                            address_range: None,
987                            relocation_overrides: &BTreeMap::new(),
988                        },
989                        options,
990                    )?;
991                }
992                SectionKind::Code => {
993                    // Look for data in gaps between functions
994                    let mut symbols = symbol_map
995                        .iter_by_address(section.address_range())
996                        .filter(|(_, s)| matches!(s.kind, SymbolKind::Function(_)))
997                        .peekable();
998                    let mut gaps = vec![];
999                    while let Some((_, symbol)) = symbols.next() {
1000                        if symbol.addr >= 0x2000000 && symbol.addr < 0x2000800 {
1001                            // Secure area gaps are just random bytes
1002                            continue;
1003                        }
1004
1005                        let next_address =
1006                            symbols.peek().map(|(_, s)| s.addr).unwrap_or(section.end_address());
1007                        let end_address = symbol.addr + symbol.size(next_address);
1008                        if end_address < next_address {
1009                            gaps.push(end_address..next_address);
1010                            log::debug!(
1011                                "Found gap between functions from {end_address:#x} to {next_address:#x}"
1012                            );
1013                        }
1014                    }
1015                    for gap in gaps {
1016                        if let Some(code) = section.code(&self.code, self.base_address)? {
1017                            data::find_local_data_from_section(
1018                                section,
1019                                FindLocalDataOptions {
1020                                    sections: &self.sections,
1021                                    module_kind: self.kind,
1022                                    symbol_map,
1023                                    relocations: &mut self.relocations,
1024                                    name_prefix: &self.default_data_prefix,
1025                                    code,
1026                                    base_address: self.base_address,
1027                                    address_range: Some(gap),
1028                                    relocation_overrides: &BTreeMap::new(),
1029                                },
1030                                options,
1031                            )?;
1032                        }
1033                    }
1034                }
1035                SectionKind::Bss => {}
1036            }
1037        }
1038        Ok(())
1039    }
1040
1041    pub fn relocations(&self) -> &Relocations {
1042        &self.relocations
1043    }
1044
1045    pub fn relocations_mut(&mut self) -> &mut Relocations {
1046        &mut self.relocations
1047    }
1048
1049    pub fn sections(&self) -> &Sections {
1050        &self.sections
1051    }
1052
1053    pub fn sections_mut(&mut self) -> &mut Sections {
1054        &mut self.sections
1055    }
1056
1057    pub fn code(&self) -> &[u8] {
1058        &self.code
1059    }
1060
1061    pub fn base_address(&self) -> u32 {
1062        self.base_address
1063    }
1064
1065    pub fn end_address(&self) -> u32 {
1066        self.base_address + self.code.len() as u32 + self.bss_size()
1067    }
1068
1069    pub fn get_function(&self, addr: u32) -> Option<&Function> {
1070        self.sections.get_by_contained_address(addr).and_then(|(_, s)| s.functions().get(&addr))
1071    }
1072
1073    pub fn bss_size(&self) -> u32 {
1074        self.bss_size
1075    }
1076
1077    pub fn name(&self) -> &str {
1078        &self.name
1079    }
1080
1081    pub fn kind(&self) -> ModuleKind {
1082        self.kind
1083    }
1084
1085    pub fn signed(&self) -> bool {
1086        self.signed
1087    }
1088}
1089
1090#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
1091pub enum ModuleKind {
1092    Arm9,
1093    Overlay(u16),
1094    Autoload(AutoloadKind),
1095}
1096
1097impl Display for ModuleKind {
1098    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1099        match self {
1100            ModuleKind::Arm9 => write!(f, "ARM9 main"),
1101            ModuleKind::Overlay(index) => write!(f, "overlay {index}"),
1102            ModuleKind::Autoload(kind) => match kind {
1103                AutoloadKind::Itcm => write!(f, "ITCM"),
1104                AutoloadKind::Dtcm => write!(f, "DTCM"),
1105                AutoloadKind::Unknown(index) => write!(f, "autoload {index}"),
1106            },
1107        }
1108    }
1109}
1110
1111struct FoundFunctions {
1112    functions: BTreeMap<u32, Function>,
1113    start: u32,
1114    end: u32,
1115}
1116
1117/// Sorted list of .init function addresses
1118struct InitFunctions(BTreeSet<u32>);
1119
1120pub struct AnalysisOptions {
1121    /// Generates function symbols when a local function call doesn't lead to a known function. This can happen if the
1122    /// destination function is encrypted or otherwise wasn't found during function analysis.
1123    pub allow_unknown_function_calls: bool,
1124    /// If true, every relocation in relocs.txt will have a comment explaining where/why it was generated.
1125    pub provide_reloc_source: bool,
1126}