From e1e754d0f5193a2bb7fda52824e8dcbaa3b5df7b Mon Sep 17 00:00:00 2001 From: RibShark <1957489+RibShark@users.noreply.github.com> Date: Sat, 22 Nov 2025 23:13:47 +0000 Subject: [PATCH] Implement DVP relocations in elf32-mips.c Added DVP relocation types and handling for MIPS ELF. Fixes issue mentioned here: https://github.com/ethteck/splat/pull/493#issuecomment-3563175240 --- bfd/elf32-mips.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c index f6bcdbb532e..7ea146b18c4 100644 --- a/bfd/elf32-mips.c +++ b/bfd/elf32-mips.c @@ -71,6 +71,8 @@ static bool mips_elf_is_local_label_name (bfd *, const char *); static bfd_reloc_status_type mips16_gprel_reloc (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type dvp_u15_s3_reloc + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); static bfd_reloc_status_type mips_elf_final_gp (bfd *, asymbol *, bool, char **, bfd_vma *); static bool mips_elf_assign_gp @@ -1676,6 +1678,69 @@ static reloc_howto_type elf_mips15_s3_howto = 0x001fffc0, /* dst_mask */ false); /* pcrel_offset */ +/* DVP relocations. + Note that partial_inplace and pcrel_offset are backwards from the + mips port. This is intentional as it seems more reasonable. */ +static reloc_howto_type elf_mips_dvp_11_pcrel_howto = + HOWTO (R_MIPS_DVP_11_PCREL, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 11, /* bitsize */ + true, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_DVP_11_PCREL", /* name */ + false, /* partial_inplace */ + 0x7ff, /* src_mask */ + 0x7ff, /* dst_mask */ + true); /* pcrel_offset */ + +static reloc_howto_type elf_mips_dvp_27_s4_howto = + HOWTO (R_MIPS_DVP_27_S4, /* type */ + 4, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 27, /* bitsize */ + false, /* pc_relative */ + 4, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_DVP_27_S4", /* name */ + false, /* partial_inplace */ + 0x7ffffff0, /* src_mask */ + 0x7ffffff0, /* dst_mask */ + false); /* pcrel_offset */ + +static reloc_howto_type elf_mips_dvp_11_s4_howto = + HOWTO (R_MIPS_DVP_11_S4, /* type */ + 4, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 11, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_signed, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MIPS_DVP_11_S4", /* name */ + false, /* partial_inplace */ + 0x03ff, /* src_mask */ + 0x03ff, /* dst_mask */ + false); /* pcrel_offset */ + +static reloc_howto_type elf_mips_dvp_u15_s3_howto = + HOWTO (R_MIPS_DVP_U15_S3, /* type */ + 3, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 15, /* bitsize */ + false, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + dvp_u15_s3_reloc, /* special_function */ + "R_MIPS_DVP_U15_S3", /* name */ + false, /* partial_inplace */ + 0xf03ff, /* src_mask */ + 0xf03ff, /* dst_mask */ + false); /* pcrel_offset */ + /* Set the GP value for OUTPUT_BFD. Returns FALSE if this is a dangerous relocation. */ @@ -1992,6 +2057,53 @@ mips16_gprel_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, return ret; } +/* Handle a dvp R_MIPS_DVP_U15_S3 reloc. + This is needed because the bits aren't contiguous. */ + +static bfd_reloc_status_type +dvp_u15_s3_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, bfd *output_bfd, + char **error_message) +{ + bfd_vma relocation; + bfd_vma x; + + /* If we're relocating, and this is an external symbol with no + addend, we don't want to change anything. We will only have an + addend if this is a newly created reloc, not read from an ELF + file. See bfd_elf_generic_reloc. */ + if (output_bfd != NULL + && (symbol->flags & BSF_SECTION_SYM) == 0 + /* partial_inplace is false, so this test always succeeds, + but for clarity and consistency with bfd_elf_generic_reloc + this is left as is. */ + && (! reloc_entry->howto->partial_inplace + || reloc_entry->addend == 0)) + { + reloc_entry->address += input_section->output_offset; + return bfd_reloc_ok; + } + + if (!_bfd_mips_reloc_offset_in_range (abfd, input_section, reloc_entry, + check_shuffle)) + return bfd_reloc_outofrange; + + relocation = (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset); + relocation += reloc_entry->addend; + relocation >>= 3; + + x = bfd_get_32 (abfd, (bfd_byte *) data + reloc_entry->address); + x |= (((relocation & 0x7800) << 10) + | (relocation & 0x7ff)); + bfd_put_32 (abfd, x, (bfd_byte *) data + reloc_entry->address); + + if (relocation & ~(bfd_vma) 0x7fff) + return bfd_reloc_overflow; + return bfd_reloc_ok; +} + /* A mapping from BFD reloc types to MIPS ELF reloc types. */ struct elf_reloc_map { @@ -2243,6 +2355,14 @@ mips_elf32_rtype_to_howto (bfd *abfd, return &elf_mips_eh_howto; case R_MIPS15_S3: return &elf_mips15_s3_howto; + case R_MIPS_DVP_11_PCREL: + return &elf_mips_dvp_11_pcrel_howto; + case R_MIPS_DVP_27_S4: + return &elf_mips_dvp_27_s4_howto; + case R_MIPS_DVP_11_S4: + return &elf_mips_dvp_11_s4_howto; + case R_MIPS_DVP_U15_S3: + return &elf_mips_dvp_u15_s3_howto; default: if (r_type >= R_MICROMIPS_min && r_type < R_MICROMIPS_max) howto = &elf_micromips_howto_table_rel[r_type - R_MICROMIPS_min];