use std::{
backtrace::Backtrace,
borrow::Cow,
collections::{BTreeMap, HashMap},
fmt::Display,
num::ParseIntError,
ops::Range,
};
use ds_rom::rom::raw::AutoloadKind;
use serde::Serialize;
use snafu::Snafu;
use super::{ParseContext, iter_attributes, module::Module};
use crate::{
analysis::functions::Function,
config::{CommentedLine, Comments, module::ModuleKind},
util::{bytes::FromSlice, parse::parse_u32},
};
#[derive(Clone, Copy)]
pub struct SectionIndex(pub usize);
#[derive(Clone)]
pub struct Section {
name: String,
kind: SectionKind,
start_address: u32,
end_address: u32,
alignment: u32,
functions: BTreeMap<u32, Function>,
comments: Comments,
migration_to: Option<MigrateSection>,
}
#[derive(Debug, Snafu)]
pub enum SectionError {
#[snafu(display(
"Section {name} must not end ({end_address:#010x}) before it starts ({start_address:#010x}):\n{backtrace}"
))]
EndBeforeStart { name: String, start_address: u32, end_address: u32, backtrace: Backtrace },
#[snafu(display("Section {name} aligment ({alignment}) must be a power of two:\n{backtrace}"))]
AlignmentPowerOfTwo { name: String, alignment: u32, backtrace: Backtrace },
#[snafu(display(
"Section {name} starts at a misaligned address {start_address:#010x}; the provided alignment was {alignment}:\n{backtrace}"
))]
MisalignedStart { name: String, start_address: u32, alignment: u32, backtrace: Backtrace },
}
#[derive(Debug, Snafu)]
pub enum SectionParseError {
#[snafu(display("{context}: expected section, got empty line"))]
EmptyLine { context: ParseContext },
#[snafu(transparent)]
SectionKind { source: SectionKindError },
#[snafu(display("{context}: failed to parse start address '{value}': {error}\n{backtrace}"))]
ParseStartAddress {
context: ParseContext,
value: String,
error: ParseIntError,
backtrace: Backtrace,
},
#[snafu(display("{context}: failed to parse end address '{value}': {error}\n{backtrace}"))]
ParseEndAddress {
context: ParseContext,
value: String,
error: ParseIntError,
backtrace: Backtrace,
},
#[snafu(display("{context}: failed to parse alignment '{value}': {error}\n{backtrace}"))]
ParseAlignment {
context: ParseContext,
value: String,
error: ParseIntError,
backtrace: Backtrace,
},
#[snafu(display(
"{context}: expected section attribute 'kind', 'start', 'end' or 'align' but got '{key}':\n{backtrace}"
))]
UnknownAttribute { context: ParseContext, key: String, backtrace: Backtrace },
#[snafu(display("{context}: missing '{attribute}' attribute:\n{backtrace}"))]
MissingAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
#[snafu(display("{context}: {error}"))]
Section { context: ParseContext, error: Box<SectionError> },
}
#[derive(Debug, Snafu)]
pub enum SectionInheritParseError {
#[snafu(display(
"{context}: section {name} does not exist in this file's header:\n{backtrace}"
))]
NotInHeader { context: ParseContext, name: String, backtrace: Backtrace },
#[snafu(display(
"{context}: attribute '{attribute}' should be omitted as it is inherited from this file's header"
))]
InheritedAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
#[snafu(transparent)]
SectionParse { source: SectionParseError },
#[snafu(transparent)]
Section { source: SectionError },
#[snafu(transparent)]
MigrateSection { source: MigrateSectionError },
}
#[derive(Debug, Snafu)]
pub enum SectionCodeError {
#[snafu(display(
"section {name} starts at {actual:#010x} before base address {expected:#010x}:\n{backtrace}"
))]
StartsBeforeBaseAddress { name: String, actual: u32, expected: u32, backtrace: Backtrace },
#[snafu(display("section ends after code ends:\n{backtrace}"))]
EndsOutsideModule { backtrace: Backtrace },
}
pub struct SectionOptions {
pub name: String,
pub kind: SectionKind,
pub start_address: u32,
pub end_address: u32,
pub alignment: u32,
pub functions: Option<BTreeMap<u32, Function>>,
pub comments: Comments,
}
pub struct SectionInheritOptions {
pub start_address: u32,
pub end_address: u32,
pub comments: Comments,
pub migration: Option<MigrateSection>,
}
impl Section {
pub fn new(options: SectionOptions) -> Result<Self, SectionError> {
let SectionOptions {
name,
kind,
start_address,
end_address,
alignment,
functions,
comments,
} = options;
if end_address < start_address {
return EndBeforeStartSnafu { name, start_address, end_address }.fail();
}
if !alignment.is_power_of_two() {
return AlignmentPowerOfTwoSnafu { name, alignment }.fail();
}
let misalign_mask = alignment - 1;
if (start_address & misalign_mask) != 0 {
return MisalignedStartSnafu { name, start_address, alignment }.fail();
}
let functions = functions.unwrap_or_else(BTreeMap::new);
Ok(Self {
name,
kind,
start_address,
end_address,
alignment,
functions,
comments,
migration_to: None,
})
}
pub fn inherit(other: &Section, options: SectionInheritOptions) -> Result<Self, SectionError> {
let SectionInheritOptions { start_address, end_address, comments, migration } = options;
if end_address < start_address {
return EndBeforeStartSnafu { name: other.name.clone(), start_address, end_address }
.fail();
}
Ok(Self {
name: other.name.clone(),
kind: other.kind,
start_address,
end_address,
alignment: other.alignment,
functions: BTreeMap::new(),
comments,
migration_to: migration,
})
}
pub(crate) fn parse(
line: &CommentedLine,
context: &ParseContext,
) -> Result<Self, SectionParseError> {
let mut words = line.text.split_whitespace();
let Some(name) = words.next() else {
return EmptyLineSnafu { context: context.clone() }.fail();
};
let mut kind = None;
let mut start = None;
let mut end = None;
let mut align = None;
for (key, value) in iter_attributes(words) {
match key {
"kind" => kind = Some(SectionKind::parse(value, context)?),
"start" => {
start = Some(parse_u32(value).map_err(|error| {
ParseStartAddressSnafu { context, value, error }.build()
})?);
}
"end" => {
end =
Some(parse_u32(value).map_err(|error| {
ParseEndAddressSnafu { context, value, error }.build()
})?)
}
"align" => {
align =
Some(parse_u32(value).map_err(|error| {
ParseAlignmentSnafu { context, value, error }.build()
})?);
}
_ => return UnknownAttributeSnafu { context: context.clone(), key }.fail(),
}
}
let kind =
kind.ok_or_else(|| MissingAttributeSnafu { context, attribute: "kind" }.build())?;
let start_address =
start.ok_or_else(|| MissingAttributeSnafu { context, attribute: "start" }.build())?;
let end_address =
end.ok_or_else(|| MissingAttributeSnafu { context, attribute: "end" }.build())?;
let alignment =
align.ok_or_else(|| MissingAttributeSnafu { context, attribute: "align" }.build())?;
Section::new(SectionOptions {
name: name.to_string(),
kind,
start_address,
end_address,
alignment,
functions: None,
comments: line.comments.clone(),
})
.map_err(|error| SectionSnafu { context, error }.build())
}
pub(crate) fn parse_inherit(
line: &CommentedLine,
context: &ParseContext,
sections: &Sections,
) -> Result<Self, SectionInheritParseError> {
let mut words = line.text.split_whitespace();
let Some(name) = words.next() else {
return EmptyLineSnafu { context: context.clone() }.fail()?;
};
let migrate_section = MigrateSection::parse(name)?;
let inherit_section = match migrate_section {
None => Some(
sections
.by_name(name)
.map(|(_, section)| section)
.ok_or_else(|| NotInHeaderSnafu { context, name }.build())?,
),
Some(_) => None,
};
let mut start = None;
let mut end = None;
for (key, value) in iter_attributes(words) {
match key {
"kind" => return InheritedAttributeSnafu { context, attribute: "kind" }.fail(),
"start" => {
start = Some(parse_u32(value).map_err(|error| {
ParseStartAddressSnafu { context, value, error }.build()
})?);
}
"end" => {
end =
Some(parse_u32(value).map_err(|error| {
ParseEndAddressSnafu { context, value, error }.build()
})?)
}
"align" => return InheritedAttributeSnafu { context, attribute: "align" }.fail(),
_ => return UnknownAttributeSnafu { context, key }.fail()?,
}
}
let start =
start.ok_or_else(|| MissingAttributeSnafu { context, attribute: "start" }.build())?;
let end = end.ok_or_else(|| MissingAttributeSnafu { context, attribute: "end" }.build())?;
match migrate_section {
None => {
let inherit_section = inherit_section.unwrap();
Ok(Section::inherit(inherit_section, SectionInheritOptions {
start_address: start,
end_address: end,
comments: line.comments.clone(),
migration: None,
})
.map_err(|error| SectionSnafu { context, error }.build())?)
}
Some(migrate_section) => Ok(Section::new(SectionOptions {
name: name.to_string(),
kind: migrate_section.section_kind(),
start_address: start,
end_address: end,
alignment: 4,
functions: None,
comments: line.comments.clone(),
})?),
}
}
pub fn code_from_module<'a>(
&'a self,
module: &'a Module,
) -> Result<Option<&'a [u8]>, SectionCodeError> {
self.code(module.code(), module.base_address())
}
pub fn code<'a>(
&'a self,
code: &'a [u8],
base_address: u32,
) -> Result<Option<&'a [u8]>, SectionCodeError> {
if self.kind() == SectionKind::Bss {
return Ok(None);
}
if self.start_address() < base_address {
return StartsBeforeBaseAddressSnafu {
name: self.name.clone(),
actual: self.start_address(),
expected: base_address,
}
.fail();
}
let start = self.start_address() - base_address;
let end = self.end_address() - base_address;
if end as usize > code.len() {
return EndsOutsideModuleSnafu.fail();
}
Ok(Some(&code[start as usize..end as usize]))
}
pub fn size(&self) -> u32 {
self.end_address - self.start_address
}
pub fn iter_words<'a>(
&'a self,
code: &'a [u8],
range: Option<Range<u32>>,
) -> impl Iterator<Item = Word> + 'a {
let range = range.unwrap_or(self.address_range());
let start = range.start.next_multiple_of(4);
let end = range.end & !3;
(start..end).step_by(4).map(move |address| {
let offset = address - self.start_address();
let bytes = &code[offset as usize..];
Word { address, value: u32::from_le_slice(bytes) }
})
}
pub fn name(&self) -> &str {
&self.name
}
pub fn kind(&self) -> SectionKind {
self.kind
}
pub fn start_address(&self) -> u32 {
self.start_address
}
pub fn set_start_address(&mut self, start_address: u32) {
self.start_address = start_address;
}
pub fn end_address(&self) -> u32 {
self.end_address
}
pub fn set_end_address(&mut self, end_address: u32) {
self.end_address = end_address;
}
pub fn address_range(&self) -> Range<u32> {
self.start_address..self.end_address
}
pub fn alignment(&self) -> u32 {
self.alignment
}
pub fn set_alignment(&mut self, alignment: u32) {
self.alignment = alignment;
}
pub fn overlaps_with(&self, other: &Section) -> bool {
self.start_address < other.end_address && other.start_address < self.end_address
}
pub fn functions(&self) -> &BTreeMap<u32, Function> {
&self.functions
}
pub fn functions_mut(&mut self) -> &mut BTreeMap<u32, Function> {
&mut self.functions
}
pub fn add_function(&mut self, function: Function) {
self.functions.insert(function.start_address(), function);
}
pub(crate) fn write_inherit(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
write!(f, "{}", self.comments.display_pre_comments())?;
write!(
f,
" {:11} start:{:#010x} end:{:#010x}",
self.name, self.start_address, self.end_address
)?;
write!(f, "{}", self.comments.display_post_comment())?;
Ok(())
}
pub fn source_name(&self) -> Cow<'_, str> {
if let Some(migration) = &self.migration_to {
migration.source_name()
} else {
Cow::Borrowed(&self.name)
}
}
pub fn migration(&self) -> Option<MigrateSection> {
self.migration_to
}
}
impl Display for Section {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.comments.display_pre_comments())?;
write!(
f,
" {:11} start:{:#010x} end:{:#010x} kind:{} align:{}",
self.name, self.start_address, self.end_address, self.kind, self.alignment
)?;
write!(f, "{}", self.comments.display_post_comment())?;
Ok(())
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum MigrateSection {
Dtcm,
Itcm,
AutoloadData(u32),
AutoloadBss(u32),
}
const DTCM_SECTION: &str = ".dtcm";
const ITCM_SECTION: &str = ".itcm";
const AUTOLOAD_DATA_SECTION_PREFIX: &str = ".autodata_";
const AUTOLOAD_BSS_SECTION_PREFIX: &str = ".autobss_";
#[derive(Debug, Snafu)]
pub enum MigrateSectionError {
#[snafu(display("Failed to parse autoload index from section '{name}': {error}\n{backtrace}"))]
InvalidAutoloadIndex { name: String, error: ParseIntError, backtrace: Backtrace },
}
impl MigrateSection {
pub fn parse(name: &str) -> Result<Option<Self>, MigrateSectionError> {
match name {
DTCM_SECTION => Ok(Some(Self::Dtcm)),
ITCM_SECTION => Ok(Some(Self::Itcm)),
_ => {
if let Some(index) = name.strip_prefix(AUTOLOAD_DATA_SECTION_PREFIX) {
let index: u32 = index.parse().map_err(|error| {
InvalidAutoloadIndexSnafu { name: name.to_string(), error }.build()
})?;
Ok(Some(Self::AutoloadData(index)))
} else if let Some(index) = name.strip_prefix(AUTOLOAD_BSS_SECTION_PREFIX) {
let index: u32 = index.parse().map_err(|error| {
InvalidAutoloadIndexSnafu { name: name.to_string(), error }.build()
})?;
Ok(Some(Self::AutoloadBss(index)))
} else {
Ok(None)
}
}
}
}
pub fn source_name(&self) -> Cow<'static, str> {
match self {
MigrateSection::Dtcm => DTCM_SECTION.into(),
MigrateSection::Itcm => ITCM_SECTION.into(),
MigrateSection::AutoloadData(index) => {
format!("{AUTOLOAD_DATA_SECTION_PREFIX}{index}").into()
}
MigrateSection::AutoloadBss(index) => {
format!("{AUTOLOAD_BSS_SECTION_PREFIX}{index}").into()
}
}
}
pub fn target_name(&self) -> &str {
match self {
MigrateSection::Dtcm => ".bss",
MigrateSection::Itcm => ".text",
MigrateSection::AutoloadData(_) => ".data",
MigrateSection::AutoloadBss(_) => ".bss",
}
}
pub fn sections_to_migrate(module_kind: ModuleKind) -> Vec<MigrateSection> {
match module_kind {
ModuleKind::Arm9 => vec![],
ModuleKind::Overlay(_) => vec![],
ModuleKind::Autoload(AutoloadKind::Dtcm) => vec![MigrateSection::Dtcm],
ModuleKind::Autoload(AutoloadKind::Itcm) => vec![MigrateSection::Itcm],
ModuleKind::Autoload(AutoloadKind::Unknown(index)) => {
vec![MigrateSection::AutoloadData(index), MigrateSection::AutoloadBss(index)]
}
}
}
pub fn section_kind(&self) -> SectionKind {
match self {
MigrateSection::Dtcm => SectionKind::Bss,
MigrateSection::Itcm => SectionKind::Code,
MigrateSection::AutoloadData(_) => SectionKind::Data,
MigrateSection::AutoloadBss(_) => SectionKind::Bss,
}
}
pub fn module_kind(&self) -> ModuleKind {
match self {
MigrateSection::Dtcm => ModuleKind::Autoload(AutoloadKind::Dtcm),
MigrateSection::Itcm => ModuleKind::Autoload(AutoloadKind::Itcm),
MigrateSection::AutoloadData(index) | MigrateSection::AutoloadBss(index) => {
ModuleKind::Autoload(AutoloadKind::Unknown(*index))
}
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Serialize)]
pub enum SectionKind {
Code,
Data,
Rodata,
Bss,
}
#[derive(Debug, Snafu)]
pub enum SectionKindError {
#[snafu(display("{context}: unknown section kind '{value}', must be one of: code, data, bss"))]
UnknownKind { context: ParseContext, value: String, backtrace: Backtrace },
}
impl SectionKind {
pub fn parse(value: &str, context: &ParseContext) -> Result<Self, SectionKindError> {
match value {
"code" => Ok(Self::Code),
"data" => Ok(Self::Data),
"rodata" => Ok(Self::Rodata),
"bss" => Ok(Self::Bss),
_ => UnknownKindSnafu { context, value }.fail(),
}
}
pub fn is_initialized(self) -> bool {
match self {
SectionKind::Code => true,
SectionKind::Data => true,
SectionKind::Rodata => true,
SectionKind::Bss => false,
}
}
pub fn is_writeable(self) -> bool {
match self {
SectionKind::Code => false,
SectionKind::Data => true,
SectionKind::Rodata => false,
SectionKind::Bss => true,
}
}
pub fn is_executable(self) -> bool {
match self {
SectionKind::Code => true,
SectionKind::Data => false,
SectionKind::Rodata => false,
SectionKind::Bss => false,
}
}
}
impl Display for SectionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Code => write!(f, "code"),
Self::Data => write!(f, "data"),
Self::Rodata => write!(f, "rodata"),
Self::Bss => write!(f, "bss"),
}
}
}
#[derive(Clone)]
pub struct Sections {
sections: Vec<Section>,
sections_by_name: HashMap<String, SectionIndex>,
}
#[derive(Debug, Snafu)]
pub enum SectionsError {
#[snafu(display("Section '{name}' already exists:\n{backtrace}"))]
DuplicateName { name: String, backtrace: Backtrace },
#[snafu(display("Section '{name}' overlaps with '{other_name}':\n{backtrace}"))]
Overlapping { name: String, other_name: String, backtrace: Backtrace },
}
impl Sections {
pub fn new() -> Self {
Self { sections: vec![], sections_by_name: HashMap::new() }
}
pub fn from_sections(section_vec: Vec<Section>) -> Result<Self, SectionsError> {
let mut sections = Self::new();
for section in section_vec {
sections.add(section)?;
}
Ok(sections)
}
pub fn add(&mut self, section: Section) -> Result<SectionIndex, SectionsError> {
if self.sections_by_name.contains_key(§ion.name) {
return DuplicateNameSnafu { name: section.name }.fail();
}
for other in &self.sections {
if section.overlaps_with(other) {
return OverlappingSnafu { name: section.name, other_name: other.name.clone() }
.fail();
}
}
let index = SectionIndex(self.sections.len());
self.sections_by_name.insert(section.name.clone(), index);
self.sections.push(section);
Ok(index)
}
pub fn remove(&mut self, name: &str) -> Option<Section> {
let index = self.sections_by_name.remove(name)?;
let section = self.sections.remove(index.0);
for (i, section) in self.sections.iter().enumerate() {
self.sections_by_name.insert(section.name.clone(), SectionIndex(i));
}
Some(section)
}
pub fn get(&self, index: SectionIndex) -> &Section {
&self.sections[index.0]
}
pub fn get_mut(&mut self, index: SectionIndex) -> &mut Section {
&mut self.sections[index.0]
}
pub fn by_name(&self, name: &str) -> Option<(SectionIndex, &Section)> {
let &index = self.sections_by_name.get(name)?;
Some((index, &self.sections[index.0]))
}
pub fn by_name_mut(&mut self, name: &str) -> Option<(SectionIndex, &mut Section)> {
let &index = self.sections_by_name.get(name)?;
Some((index, &mut self.sections[index.0]))
}
pub fn iter(&self) -> impl Iterator<Item = &Section> {
self.sections.iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Section> {
self.sections.iter_mut()
}
pub fn len(&self) -> usize {
self.sections.len()
}
pub fn get_by_contained_address(&self, address: u32) -> Option<(SectionIndex, &Section)> {
self.sections
.iter()
.enumerate()
.find(|(_, s)| address >= s.start_address && address < s.end_address)
.map(|(i, s)| (SectionIndex(i), s))
}
pub fn get_by_contained_address_mut(
&mut self,
address: u32,
) -> Option<(SectionIndex, &mut Section)> {
self.sections
.iter_mut()
.enumerate()
.find(|(_, s)| address >= s.start_address && address < s.end_address)
.map(|(i, s)| (SectionIndex(i), s))
}
pub fn add_function(&mut self, function: Function) {
let address = function.first_instruction_address();
self.sections
.iter_mut()
.find(|s| address >= s.start_address && address < s.end_address)
.unwrap()
.functions
.insert(address, function);
}
pub fn sorted_by_address(&self) -> Vec<&Section> {
let mut sections = self.sections.iter().collect::<Vec<_>>();
sections.sort_unstable_by(|a, b| {
a.start_address.cmp(&b.start_address).then(a.end_address.cmp(&b.end_address))
});
sections
}
pub fn functions(&self) -> impl Iterator<Item = &Function> {
self.sections.iter().flat_map(|s| s.functions.values())
}
pub fn functions_mut(&mut self) -> impl Iterator<Item = &mut Function> {
self.sections.iter_mut().flat_map(|s| s.functions.values_mut())
}
pub fn base_address(&self) -> Option<u32> {
self.sections.iter().map(|s| s.start_address).min()
}
pub fn end_address(&self) -> Option<u32> {
self.sections.iter().map(|s| s.end_address).max()
}
pub fn text_size(&self) -> u32 {
self.sections.iter().filter(|s| s.kind != SectionKind::Bss).map(Section::size).sum()
}
pub fn bss_size(&self) -> u32 {
self.sections.iter().filter(|s| s.kind == SectionKind::Bss).map(Section::size).sum()
}
pub fn bss_range(&self) -> Option<Range<u32>> {
self.sections
.iter()
.filter(|s| s.kind == SectionKind::Bss)
.map(Section::address_range)
.reduce(|a, b| a.start.min(b.start)..a.end.max(b.end))
}
pub fn get_section_after(&self, text_end: u32) -> Option<&Section> {
self.sorted_by_address().iter().copied().find(|s| s.start_address >= text_end)
}
}
impl IntoIterator for Sections {
type IntoIter = <Vec<Self::Item> as IntoIterator>::IntoIter;
type Item = Section;
fn into_iter(self) -> Self::IntoIter {
self.sections.into_iter()
}
}
pub struct Word {
pub address: u32,
pub value: u32,
}