use core::fmt;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;
use crate::{
addresses::Vram,
collections::addended_ordered_map::FindSettings,
config::Compiler,
context::Context,
metadata::{LabelMetadata, LabelType, SymbolMetadata, SymbolType},
parent_segment_info::ParentSegmentInfo,
symbols::display::InternalSymDisplSettings,
};
use super::{RelocReferencedSym, RelocationType};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "pyo3", pyclass(module = "spimdisasm"))]
pub struct RelocationInfo {
reloc_type: RelocationType,
referenced_sym: RelocReferencedSym,
}
impl RelocationInfo {
#[must_use]
pub(crate) fn new(reloc_type: RelocationType, referenced_sym: RelocReferencedSym) -> Self {
Self {
reloc_type,
referenced_sym,
}
}
#[must_use]
pub(crate) const fn reloc_type(&self) -> RelocationType {
self.reloc_type
}
#[must_use]
pub(crate) fn display<'ctx, 'rel, 'prnt>(
&'rel self,
context: &'ctx Context,
segment_info: &'prnt ParentSegmentInfo,
allow_ref_with_addend: bool,
compiler: Option<Compiler>,
internal_settings: InternalSymDisplSettings,
) -> Option<RelocationInfoDisplay<'ctx, 'rel, 'prnt>> {
RelocationInfoDisplay::new(
context,
self,
segment_info,
allow_ref_with_addend,
compiler,
internal_settings,
)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, PartialOrd)]
enum RelocSymState<'name, 'meta> {
LiteralSymName(&'name str, i64),
Sym(Vram, &'meta SymbolMetadata),
Label(Vram, &'meta LabelMetadata),
SegmentNotFound(Vram),
LabelNotFound(Vram),
}
#[derive(Debug, Clone, Copy, Hash, PartialEq)]
pub struct RelocationInfoDisplay<'ctx, 'rel, 'prnt> {
rel: &'rel RelocationInfo,
segment_info: &'prnt ParentSegmentInfo,
reloc_sym_state: RelocSymState<'rel, 'ctx>,
compiler: Option<Compiler>,
internal_settings: InternalSymDisplSettings,
}
impl<'ctx, 'rel, 'prnt> RelocationInfoDisplay<'ctx, 'rel, 'prnt> {
pub(crate) fn new(
context: &'ctx Context,
rel: &'rel RelocationInfo,
segment_info: &'prnt ParentSegmentInfo,
allow_ref_with_addend: bool,
compiler: Option<Compiler>,
internal_settings: InternalSymDisplSettings,
) -> Option<Self> {
let reloc_sym_state = match &rel.referenced_sym {
RelocReferencedSym::SymName(name, addend) => {
RelocSymState::LiteralSymName(name, *addend)
}
RelocReferencedSym::Address {
unaddended_vram,
addended_vram,
} => {
let find_settings = FindSettings::new(
allow_ref_with_addend && rel.reloc_type.allow_addends_on_ref(),
);
if let Some(sym_metadata) = context.find_symbol_from_any_segment(
*unaddended_vram,
segment_info,
find_settings,
|metadata| {
match rel.reloc_type {
RelocationType::R_MIPS_NONE => true,
RelocationType::R_MIPS_16 => true,
RelocationType::R_MIPS_32 => {
if metadata.sym_type() == Some(SymbolType::Function) {
metadata.vram() == *unaddended_vram
} else {
true
}
}
RelocationType::R_MIPS_REL32 => true,
RelocationType::R_MIPS_26 => {
metadata.sym_type() == Some(SymbolType::Function)
}
RelocationType::R_MIPS_HI16 | RelocationType::R_MIPS_LO16 => {
if metadata.sym_type() == Some(SymbolType::Function) {
metadata.vram() == *unaddended_vram
} else {
true
}
}
RelocationType::R_MIPS_GPREL16 => true,
RelocationType::R_MIPS_LITERAL => true,
RelocationType::R_MIPS_PC16 => {
metadata.sym_type() == Some(SymbolType::Function)
}
RelocationType::R_MIPS_CALL16
| RelocationType::R_MIPS_CALL_HI16
| RelocationType::R_MIPS_CALL_LO16 => {
metadata.sym_type() == Some(SymbolType::Function)
}
RelocationType::R_MIPS_GOT16
| RelocationType::R_MIPS_GPREL32
| RelocationType::R_MIPS_GOT_HI16
| RelocationType::R_MIPS_GOT_LO16 => {
true
}
RelocationType::R_CUSTOM_CONSTANT_HI => false,
RelocationType::R_CUSTOM_CONSTANT_LO => false,
}
},
) {
RelocSymState::Sym(*addended_vram, sym_metadata)
} else {
if false {
return None;
}
RelocSymState::SegmentNotFound(*addended_vram)
}
}
RelocReferencedSym::Label(vram) => {
if let Some(label_metadata) =
context.find_label_from_any_segment(*vram, segment_info, |metadata| {
match rel.reloc_type {
RelocationType::R_MIPS_NONE => true,
RelocationType::R_MIPS_16 => true,
RelocationType::R_MIPS_32 => metadata.label_type() != LabelType::Branch,
RelocationType::R_MIPS_REL32 => true,
RelocationType::R_MIPS_26 => true,
RelocationType::R_MIPS_HI16 => true,
RelocationType::R_MIPS_LO16 => true,
RelocationType::R_MIPS_GPREL16 => true,
RelocationType::R_MIPS_LITERAL => true,
RelocationType::R_MIPS_PC16 => true,
RelocationType::R_MIPS_CALL16
| RelocationType::R_MIPS_CALL_HI16
| RelocationType::R_MIPS_CALL_LO16 => {
metadata.label_type() == LabelType::AlternativeEntry
}
RelocationType::R_MIPS_GOT16
| RelocationType::R_MIPS_GPREL32
| RelocationType::R_MIPS_GOT_HI16
| RelocationType::R_MIPS_GOT_LO16 => {
true
}
RelocationType::R_CUSTOM_CONSTANT_HI => false,
RelocationType::R_CUSTOM_CONSTANT_LO => false,
}
})
{
RelocSymState::Label(*vram, label_metadata)
} else {
if false {
return None;
}
RelocSymState::LabelNotFound(*vram)
}
}
};
Some(Self {
rel,
segment_info,
reloc_sym_state,
compiler,
internal_settings,
})
}
}
impl RelocationInfoDisplay<'_, '_, '_> {
fn display_addend(&self, f: &mut fmt::Formatter<'_>, addend: i64) -> fmt::Result {
if addend == 0 {
Ok(())
} else {
if self
.compiler
.is_some_and(|x| x.big_addend_workaround_for_migrated_functions())
&& self.internal_settings.migrate()
&& self.rel.reloc_type == RelocationType::R_MIPS_LO16
{
if addend < -0x8000 {
return write!(f, " - (0x{:X} & 0xFFFF)", -addend);
}
if addend > 0x7FFF {
return write!(f, " + (0x{addend:X} & 0xFFFF)");
}
}
if addend < 0 {
write!(f, " - 0x{:X}", -addend)
} else {
write!(f, " + 0x{addend:X}")
}
}
}
}
impl fmt::Display for RelocationInfoDisplay<'_, '_, '_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let x = match self.rel.reloc_type {
RelocationType::R_MIPS_NONE => "",
RelocationType::R_MIPS_16 => "%half",
RelocationType::R_MIPS_32 => ".word ",
RelocationType::R_MIPS_REL32 => "",
RelocationType::R_MIPS_26 => "",
RelocationType::R_MIPS_HI16 => "%hi",
RelocationType::R_MIPS_LO16 => "%lo",
RelocationType::R_MIPS_GPREL16 => "%gp_rel",
RelocationType::R_MIPS_LITERAL => "",
RelocationType::R_MIPS_GOT16 => "%got",
RelocationType::R_MIPS_PC16 => "",
RelocationType::R_MIPS_CALL16 => "%call16",
RelocationType::R_MIPS_GPREL32 => ".gpword ",
RelocationType::R_MIPS_GOT_HI16 => "%got_hi",
RelocationType::R_MIPS_GOT_LO16 => "%got_lo",
RelocationType::R_MIPS_CALL_HI16 => "%call_hi",
RelocationType::R_MIPS_CALL_LO16 => "%call_lo",
RelocationType::R_CUSTOM_CONSTANT_HI => "",
RelocationType::R_CUSTOM_CONSTANT_LO => "",
};
write!(f, "{x}")?;
if self.rel.reloc_type.uses_parenthesis() {
write!(f, "(")?;
}
let addend = match &self.reloc_sym_state {
RelocSymState::LiteralSymName(name, addend) => {
write!(f, "{name}")?;
*addend
}
RelocSymState::Sym(vram, sym_metadata) => {
write!(f, "{}", sym_metadata.display_name())?;
(*vram - sym_metadata.vram()).inner().into()
}
RelocSymState::Label(_vram, label_metadata) => {
write!(f, "{}", label_metadata.display_name())?;
0
}
RelocSymState::SegmentNotFound(vram) => {
write!(f, "/* ERROR: segment for address 0x{vram} not found */")?;
0
}
RelocSymState::LabelNotFound(vram) => {
write!(f, "/* ERROR: label for address 0x{vram} not found */")?;
0
}
};
self.display_addend(f, addend)?;
let x = match self.rel.reloc_type {
RelocationType::R_MIPS_NONE => "",
RelocationType::R_MIPS_16 => "",
RelocationType::R_MIPS_32 => "",
RelocationType::R_MIPS_REL32 => "",
RelocationType::R_MIPS_26 => "",
RelocationType::R_MIPS_HI16 => "",
RelocationType::R_MIPS_LO16 => "",
RelocationType::R_MIPS_GPREL16 => "",
RelocationType::R_MIPS_LITERAL => "",
RelocationType::R_MIPS_GOT16 => "",
RelocationType::R_MIPS_PC16 => "",
RelocationType::R_MIPS_CALL16 => "",
RelocationType::R_MIPS_GPREL32 => "",
RelocationType::R_MIPS_GOT_HI16 => "",
RelocationType::R_MIPS_GOT_LO16 => "",
RelocationType::R_MIPS_CALL_HI16 => "",
RelocationType::R_MIPS_CALL_LO16 => "",
RelocationType::R_CUSTOM_CONSTANT_HI => " >> 16",
RelocationType::R_CUSTOM_CONSTANT_LO => " & 0xFFFF",
};
write!(f, "{x}")?;
if self.rel.reloc_type.uses_parenthesis() {
write!(f, ")")?;
}
Ok(())
}
}