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