1use std::{
2 backtrace::Backtrace,
3 collections::{BTreeMap, btree_map},
4 fmt::Display,
5 io::{self, BufWriter, Write},
6 iter,
7 num::ParseIntError,
8 ops::Range,
9 path::Path,
10};
11
12use ds_rom::rom::raw::AutoloadKind;
13use serde::{Deserialize, Serialize};
14use snafu::Snafu;
15
16use super::{
17 ParseContext, iter_attributes,
18 module::{Module, ModuleKind},
19};
20use crate::{
21 config::{
22 CommentedLine, Comments,
23 link_time_const::{LinkTimeConst, LinkTimeConstParseError},
24 symbol::{Symbol, SymbolMap},
25 },
26 util::{
27 io::{FileError, create_file},
28 parse::{parse_i32, parse_u16, parse_u32},
29 },
30};
31
32pub struct Relocations {
33 relocations: BTreeMap<u32, Relocation>,
35 relocations_by_to_address: BTreeMap<u32, Vec<u32>>,
37}
38
39#[derive(Debug, Snafu)]
40pub enum RelocationsParseError {
41 #[snafu(transparent)]
42 File { source: FileError },
43 #[snafu(transparent)]
44 Io { source: io::Error },
45 #[snafu(transparent)]
46 RelocationParse { source: RelocationParseError },
47 #[snafu(transparent)]
48 Relocations { source: RelocationsError },
49}
50
51#[derive(Debug, Snafu)]
52pub enum RelocationsWriteError {
53 #[snafu(transparent)]
54 File { source: FileError },
55 #[snafu(transparent)]
56 Io { source: io::Error },
57}
58
59#[derive(Debug, Snafu)]
60pub enum RelocationsError {
61 #[snafu(display(
62 "Relocation from {from:#010x} to {curr_to:#010x} in {curr_module} collides with existing one to {prev_to:#010x} in {prev_module}"
63 ))]
64 RelocationCollision {
65 from: u32,
66 curr_to: u32,
67 curr_module: RelocationModule,
68 prev_to: u32,
69 prev_module: RelocationModule,
70 },
71}
72
73impl Relocations {
74 pub fn new() -> Self {
75 Self { relocations: BTreeMap::new(), relocations_by_to_address: BTreeMap::new() }
76 }
77
78 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, RelocationsParseError> {
79 let path = path.as_ref();
80 let mut context = ParseContext { file_path: path.to_str().unwrap().to_string(), row: 0 };
81
82 let lines = CommentedLine::read(path)?;
83 let mut relocs = Self::new();
84 for line in lines {
85 let line = line?;
86 context.row = line.row;
87
88 let Some(relocation) = Relocation::parse(line, &context)? else {
89 continue;
90 };
91 relocs.add(relocation)?;
92 }
93
94 Ok(relocs)
95 }
96
97 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), RelocationsWriteError> {
98 let path = path.as_ref();
99
100 let file = create_file(path)?;
101 let mut writer = BufWriter::new(file);
102
103 for relocation in self.relocations.values() {
104 writeln!(writer, "{relocation}")?;
105 }
106 Ok(())
107 }
108
109 pub fn add(&mut self, relocation: Relocation) -> Result<&mut Relocation, RelocationsError> {
110 let relocation = match self.relocations.entry(relocation.from) {
111 btree_map::Entry::Vacant(entry) => entry.insert(relocation),
112 btree_map::Entry::Occupied(entry) => {
113 if entry.get() == &relocation {
114 log::warn!(
115 "Relocation from {:#010x} to {:#010x} in {} is identical to existing one",
116 relocation.from,
117 relocation.to,
118 relocation.module
119 );
120 entry.into_mut()
121 } else {
122 let other = entry.get();
123 let error = RelocationCollisionSnafu {
124 from: relocation.from,
125 curr_to: relocation.to,
126 curr_module: relocation.module,
127 prev_to: other.to,
128 prev_module: other.module.clone(),
129 }
130 .build();
131 log::error!("{error}");
132 return Err(error);
133 }
134 }
135 };
136
137 self.relocations_by_to_address.entry(relocation.to).or_default().push(relocation.from);
138
139 Ok(relocation)
140 }
141
142 pub fn add_call(
143 &mut self,
144 from: u32,
145 to: u32,
146 module: RelocationModule,
147 from_thumb: bool,
148 to_thumb: bool,
149 ) -> Result<&mut Relocation, RelocationsError> {
150 self.add(Relocation::new_call(from, to, module, from_thumb, to_thumb))
151 }
152
153 pub fn add_load(
154 &mut self,
155 from: u32,
156 to: u32,
157 addend: i32,
158 module: RelocationModule,
159 ) -> Result<&mut Relocation, RelocationsError> {
160 self.add(Relocation::new_load(from, to, addend, module))
161 }
162
163 pub fn get(&self, from: u32) -> Option<&Relocation> {
164 self.relocations.get(&from)
165 }
166
167 pub fn get_mut(&mut self, from: u32) -> Option<&mut Relocation> {
168 self.relocations.get_mut(&from)
169 }
170
171 pub fn iter(&self) -> impl Iterator<Item = &Relocation> {
172 self.relocations.values()
173 }
174
175 pub fn iter_range(&self, range: Range<u32>) -> impl Iterator<Item = (&u32, &Relocation)> {
176 self.relocations.range(range)
177 }
178
179 pub fn get_by_to_address(&self, to_address: u32) -> &[u32] {
181 self.relocations_by_to_address.get(&to_address).map(|v| v.as_slice()).unwrap_or(&[])
182 }
183
184 pub fn remove(&mut self, from: u32) -> Option<Relocation> {
185 self.relocations.remove(&from)
186 }
187}
188
189#[derive(PartialEq, Eq, Clone)]
190pub struct Relocation {
191 from: u32,
192 to: u32,
193 addend: i32,
194 kind: RelocationKind,
195 module: RelocationModule,
196 pub comments: Comments,
197}
198
199pub struct RelocationOptions {
200 pub from: u32,
201 pub to: u32,
202 pub addend: i32,
203 pub kind: RelocationKind,
204 pub module: RelocationModule,
205 pub comments: Comments,
206}
207
208#[derive(Debug, Snafu)]
209pub enum RelocationParseError {
210 #[snafu(display(
211 "{context}: failed to parse \"from\" address '{value}': {error}\n{backtrace}"
212 ))]
213 ParseFrom { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
214 #[snafu(display("{context}: failed to parse \"to\" address '{value}': {error}\n{backtrace}"))]
215 ParseTo { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
216 #[snafu(display("{context}: failed to parse \"add\" addend '{value}': {error}\n{backtrace}"))]
217 ParseAdd { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
218 #[snafu(transparent)]
219 RelocationKindParse { source: RelocationKindParseError },
220 #[snafu(transparent)]
221 RelocationModuleParse { source: Box<RelocationModuleParseError> },
222 #[snafu(display(
223 "{context}: expected relocation attribute 'from', 'to', 'add', 'kind' or 'module' but got '{key}':\n{backtrace}"
224 ))]
225 UnknownAttribute { context: ParseContext, key: String, backtrace: Backtrace },
226 #[snafu(display("{context}: missing '{attribute}' attribute"))]
227 MissingAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
228}
229
230impl Relocation {
231 pub fn new(options: RelocationOptions) -> Self {
232 let RelocationOptions { from, to, addend, kind, module, comments } = options;
233 Self { from, to, addend, kind, module, comments }
234 }
235
236 fn parse(
237 line: CommentedLine,
238 context: &ParseContext,
239 ) -> Result<Option<Self>, RelocationParseError> {
240 let words = line.text.split_whitespace();
241
242 let mut from = None;
243 let mut to = None;
244 let mut addend = 0;
245 let mut kind = None;
246 let mut module = None;
247 for (key, value) in iter_attributes(words) {
248 match key {
249 "from" => {
250 from = Some(
251 parse_u32(value)
252 .map_err(|error| ParseFromSnafu { context, value, error }.build())?,
253 )
254 }
255 "to" => {
256 to = Some(
257 parse_u32(value)
258 .map_err(|error| ParseToSnafu { context, value, error }.build())?,
259 )
260 }
261 "add" => {
262 addend = parse_i32(value)
263 .map_err(|error| ParseAddSnafu { context, value, error }.build())?
264 }
265 "kind" => kind = Some(RelocationKind::parse(value, context)?),
266 "module" => module = Some(RelocationModule::parse(value, context)?),
267 _ => return UnknownAttributeSnafu { context, key }.fail(),
268 }
269 }
270
271 let from =
272 from.ok_or_else(|| MissingAttributeSnafu { context, attribute: "from" }.build())?;
273 let kind =
274 kind.ok_or_else(|| MissingAttributeSnafu { context, attribute: "kind" }.build())?;
275 let to = match kind {
276 RelocationKind::LinkTimeConst(_) => 0,
278 _ => to.ok_or_else(|| MissingAttributeSnafu { context, attribute: "to" }.build())?,
279 };
280 let module = match kind {
281 RelocationKind::OverlayId | RelocationKind::LinkTimeConst(_) => RelocationModule::None,
283 _ => module
284 .ok_or_else(|| MissingAttributeSnafu { context, attribute: "module" }.build())?,
285 };
286
287 Ok(Some(Self { from, to, addend, kind, module, comments: line.comments }))
288 }
289
290 pub fn new_call(
291 from: u32,
292 to: u32,
293 module: RelocationModule,
294 from_thumb: bool,
295 to_thumb: bool,
296 ) -> Self {
297 Self {
298 from,
299 to,
300 addend: 0,
301 kind: match (from_thumb, to_thumb) {
302 (true, true) => RelocationKind::ThumbCall,
303 (true, false) => RelocationKind::ThumbCallArm,
304 (false, true) => RelocationKind::ArmCallThumb,
305 (false, false) => RelocationKind::ArmCall,
306 },
307 module,
308 comments: Comments::new(),
309 }
310 }
311
312 pub fn new_branch(from: u32, to: u32, module: RelocationModule) -> Self {
313 Self {
314 from,
315 to,
316 addend: 0,
317 kind: RelocationKind::ArmBranch,
318 module,
319 comments: Comments::new(),
320 }
321 }
322
323 pub fn new_load(from: u32, to: u32, addend: i32, module: RelocationModule) -> Self {
324 Self { from, to, addend, kind: RelocationKind::Load, module, comments: Comments::new() }
325 }
326
327 pub fn from_address(&self) -> u32 {
328 self.from
329 }
330
331 pub fn to_address(&self) -> u32 {
332 self.to
333 }
334
335 pub fn kind(&self) -> RelocationKind {
336 self.kind
337 }
338
339 pub fn set_kind(&mut self, kind: RelocationKind) {
340 self.kind = kind;
341 }
342
343 pub fn module(&self) -> &RelocationModule {
344 &self.module
345 }
346
347 pub fn set_module(&mut self, module: RelocationModule) {
348 self.module = module;
349 }
350
351 pub fn destination_module(&self) -> Option<ModuleKind> {
352 match &self.module {
353 RelocationModule::None => None,
354 RelocationModule::Overlay { id } => Some(ModuleKind::Overlay(*id)),
355 RelocationModule::Overlays { .. } => None,
356 RelocationModule::Main => Some(ModuleKind::Arm9),
357 RelocationModule::Itcm => Some(ModuleKind::Autoload(AutoloadKind::Itcm)),
358 RelocationModule::Dtcm => Some(ModuleKind::Autoload(AutoloadKind::Dtcm)),
359 RelocationModule::Autoload { index } => {
360 Some(ModuleKind::Autoload(AutoloadKind::Unknown(*index)))
361 }
362 }
363 }
364
365 pub fn addend(&self) -> i64 {
366 i64::from(self.addend) + self.kind.addend()
367 }
368
369 pub fn addend_value(&self) -> i32 {
370 self.addend
371 }
372
373 pub fn set_addend(&mut self, addend: i32) {
374 self.addend = addend;
375 }
376
377 pub fn find_symbol_location<'a>(&self, symbol_map: &'a SymbolMap) -> Option<(&'a Symbol, u32)> {
378 let symbol = symbol_map
379 .first_symbol_before(self.from_address())
380 .and_then(|symbols| (!symbols.is_empty()).then_some(symbols[0].1))?;
381 let offset = self.from_address() - symbol.addr;
382
383 Some((symbol, offset))
384 }
385}
386
387impl Display for Relocation {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 write!(f, "{}", self.comments.display_pre_comments())?;
390 write!(f, "from:{:#010x} kind:{}", self.from, self.kind)?;
391
392 match self.kind {
393 RelocationKind::OverlayId => write!(f, " to:{}", self.to)?,
394 RelocationKind::LinkTimeConst(_) => {}
395 _ => write!(f, " to:{:#010x}", self.to)?,
396 }
397
398 if self.addend != 0 {
399 write!(f, " add:{:#x}", self.addend)?;
400 }
401
402 match self.kind {
403 RelocationKind::OverlayId | RelocationKind::LinkTimeConst(_) => {}
404 _ => write!(f, " module:{}", self.module)?,
405 }
406
407 write!(f, "{}", self.comments.display_post_comment())?;
408 Ok(())
409 }
410}
411
412#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
413pub enum RelocationKind {
414 ArmCall,
415 ThumbCall,
416 ArmCallThumb,
417 ThumbCallArm,
418 ArmBranch,
419 Load,
420 OverlayId,
421 LinkTimeConst(LinkTimeConst),
422}
423
424#[derive(Debug, Snafu)]
425pub enum RelocationKindParseError {
426 #[snafu(display(
427 "{context}: unknown relocation kind '{value}', must be one of:
428 arm_call, thumb_call, arm_call_thumb, thumb_call_arm, arm_branch, load, link_time_const(...):
429 {backtrace}"
430 ))]
431 UnknownKind { context: ParseContext, value: String, backtrace: Backtrace },
432 #[snafu(transparent)]
433 LinkTimeConstParse { source: LinkTimeConstParseError },
434}
435
436impl RelocationKind {
437 fn parse(value: &str, context: &ParseContext) -> Result<Self, RelocationKindParseError> {
438 match value {
439 "arm_call" => Ok(Self::ArmCall),
440 "thumb_call" => Ok(Self::ThumbCall),
441 "arm_call_thumb" => Ok(Self::ArmCallThumb),
442 "thumb_call_arm" => Ok(Self::ThumbCallArm),
443 "arm_branch" => Ok(Self::ArmBranch),
444 "load" => Ok(Self::Load),
445 "overlay_id" => Ok(Self::OverlayId),
446 value => {
447 if let Some(link_time_const) = value.strip_prefix("link_time_const(")
448 && let Some(link_time_const) = link_time_const.strip_suffix(")")
449 {
450 Ok(Self::LinkTimeConst(LinkTimeConst::parse(link_time_const, context)?))
451 } else {
452 UnknownKindSnafu { context, value }.fail()
453 }
454 }
455 }
456 }
457
458 pub fn addend(&self) -> i64 {
459 match self {
460 Self::ArmCall => -8,
461 Self::ThumbCall => -4,
462 Self::ArmCallThumb => -8,
463 Self::ThumbCallArm => -4,
464 Self::ArmBranch => -8,
465 Self::Load => 0,
466 Self::OverlayId => 0,
467 Self::LinkTimeConst(_) => 0,
468 }
469 }
470}
471
472impl Display for RelocationKind {
473 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
474 match self {
475 Self::ArmCall => write!(f, "arm_call"),
476 Self::ThumbCall => write!(f, "thumb_call"),
477 Self::ArmCallThumb => write!(f, "arm_call_thumb"),
478 Self::ThumbCallArm => write!(f, "thumb_call_arm"),
479 Self::ArmBranch => write!(f, "arm_branch"),
480 Self::Load => write!(f, "load"),
481 Self::OverlayId => write!(f, "overlay_id"),
482 Self::LinkTimeConst(link_time_const) => write!(f, "link_time_const({link_time_const})"),
483 }
484 }
485}
486
487#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
488pub enum RelocationModule {
489 None,
490 Overlay { id: u16 },
491 Overlays { ids: Vec<u16> },
492 Main,
493 Itcm,
494 Dtcm,
495 Autoload { index: u32 },
496}
497
498#[derive(Debug, Snafu)]
499pub enum RelocationFromModulesError {
500 #[snafu(display("Relocations to {module_kind} should be unambiguous:\n{backtrace}"))]
501 AmbiguousNonOverlayRelocation { module_kind: ModuleKind, backtrace: Backtrace },
502}
503
504#[derive(Debug, Snafu)]
505pub enum RelocationModuleParseError {
506 #[snafu(display(
507 "{context}: relocations to '{module}' have no options, but got '({options})':\n{backtrace}"
508 ))]
509 UnexpectedOptions {
510 context: ParseContext,
511 module: String,
512 options: String,
513 backtrace: Backtrace,
514 },
515 #[snafu(display("{context}: failed to parse overlay ID '{value}': {error}\n{backtrace}"))]
516 ParseOverlayId {
517 context: ParseContext,
518 value: String,
519 error: ParseIntError,
520 backtrace: Backtrace,
521 },
522 #[snafu(display(
523 "{context}: relocation to 'overlays' must have two or more overlay IDs, but got {ids:?}:\n{backtrace}"
524 ))]
525 ExpectedMultipleOverlays { context: ParseContext, ids: Vec<u16>, backtrace: Backtrace },
526 #[snafu(display("{context}: failed to parse autoload index '{value}': {error}\n{backtrace}"))]
527 ParseAutoloadIndex {
528 context: ParseContext,
529 value: String,
530 error: ParseIntError,
531 backtrace: Backtrace,
532 },
533 #[snafu(display(
534 "{context}: unknown relocation to '{module}', must be one of: overlays, overlay, main, itcm, dtcm, none:\n{backtrace}"
535 ))]
536 UnknownModule { context: ParseContext, module: String, backtrace: Backtrace },
537}
538
539impl RelocationModule {
540 pub fn from_modules<'a, I>(mut modules: I) -> Result<Self, RelocationFromModulesError>
541 where
542 I: Iterator<Item = &'a Module>,
543 {
544 let Some(first) = modules.next() else { return Ok(Self::None) };
545
546 let module_kind = first.kind();
547 match module_kind {
548 ModuleKind::Arm9 => {
549 if modules.next().is_some() {
550 return AmbiguousNonOverlayRelocationSnafu { module_kind }.fail();
551 }
552 Ok(Self::Main)
553 }
554 ModuleKind::Autoload(kind) => {
555 if modules.next().is_some() {
556 return AmbiguousNonOverlayRelocationSnafu { module_kind }.fail();
557 }
558 match kind {
559 AutoloadKind::Itcm => Ok(Self::Itcm),
560 AutoloadKind::Dtcm => Ok(Self::Dtcm),
561 AutoloadKind::Unknown(index) => Ok(Self::Autoload { index }),
562 }
563 }
564 ModuleKind::Overlay(id) => {
565 let ids = iter::once(first)
566 .chain(modules)
567 .map(|module| {
568 if let ModuleKind::Overlay(id) = module.kind() {
569 Ok(id)
570 } else {
571 AmbiguousNonOverlayRelocationSnafu { module_kind: module.kind() }.fail()
572 }
573 })
574 .collect::<Result<Vec<_>, _>>()?;
575 if ids.len() > 1 {
576 Ok(Self::Overlays { ids })
577 } else {
578 Ok(Self::Overlay { id })
579 }
580 }
581 }
582 }
583
584 fn parse(text: &str, context: &ParseContext) -> Result<Self, Box<RelocationModuleParseError>> {
585 let (value, options) = text.split_once('(').unwrap_or((text, ""));
586 let options = options.strip_suffix(')').unwrap_or(options);
587
588 match value {
589 "none" => {
590 if options.is_empty() {
591 Ok(Self::None)
592 } else {
593 Err(Box::new(
594 UnexpectedOptionsSnafu { context, module: "none", options }.build(),
595 ))
596 }
597 }
598 "overlay" => Ok(Self::Overlay {
599 id: parse_u16(options).map_err(|error| {
600 ParseOverlayIdSnafu { context, value: options, error }.build()
601 })?,
602 }),
603 "overlays" => {
604 let ids = options
605 .split(',')
606 .map(|x| {
607 parse_u16(x).map_err(|error| {
608 Box::new(ParseOverlayIdSnafu { context, value: x, error }.build())
609 })
610 })
611 .collect::<Result<Vec<_>, _>>()?;
612 if ids.len() < 2 {
613 Err(Box::new(ExpectedMultipleOverlaysSnafu { context, ids }.build()))
614 } else {
615 Ok(Self::Overlays { ids })
616 }
617 }
618 "main" => {
619 if options.is_empty() {
620 Ok(Self::Main)
621 } else {
622 Err(Box::new(
623 UnexpectedOptionsSnafu { context, module: "main", options }.build(),
624 ))
625 }
626 }
627 "itcm" => {
628 if options.is_empty() {
629 Ok(Self::Itcm)
630 } else {
631 Err(Box::new(
632 UnexpectedOptionsSnafu { context, module: "itcm", options }.build(),
633 ))
634 }
635 }
636 "dtcm" => {
637 if options.is_empty() {
638 Ok(Self::Dtcm)
639 } else {
640 Err(Box::new(
641 UnexpectedOptionsSnafu { context, module: "dtcm", options }.build(),
642 ))
643 }
644 }
645 "autoload" => Ok(Self::Autoload {
646 index: parse_u32(options).map_err(|error| {
647 ParseAutoloadIndexSnafu { context, value: options, error }.build()
648 })?,
649 }),
650 _ => Err(Box::new(UnknownModuleSnafu { context, module: value }.build())),
651 }
652 }
653}
654
655impl From<ModuleKind> for RelocationModule {
656 fn from(value: ModuleKind) -> Self {
657 match value {
658 ModuleKind::Arm9 => Self::Main,
659 ModuleKind::Overlay(id) => Self::Overlay { id },
660 ModuleKind::Autoload(kind) => match kind {
661 AutoloadKind::Itcm => Self::Itcm,
662 AutoloadKind::Dtcm => Self::Dtcm,
663 AutoloadKind::Unknown(index) => Self::Autoload { index },
664 },
665 }
666 }
667}
668
669impl Display for RelocationModule {
670 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
671 match self {
672 RelocationModule::None => write!(f, "none"),
673 RelocationModule::Overlay { id } => write!(f, "overlay({id})"),
674 RelocationModule::Overlays { ids } => {
675 write!(f, "overlays({}", ids[0])?;
676 for id in &ids[1..] {
677 write!(f, ",{id}")?;
678 }
679 write!(f, ")")?;
680 Ok(())
681 }
682 RelocationModule::Main => write!(f, "main"),
683 RelocationModule::Itcm => write!(f, "itcm"),
684 RelocationModule::Dtcm => write!(f, "dtcm"),
685 RelocationModule::Autoload { index } => write!(f, "autoload({index})"),
686 }
687 }
688}