| /* Freescale S12Z-specific support for 32-bit ELF |
| Copyright (C) 1999-2024 Free Software Foundation, Inc. |
| (Heavily copied from the D10V port by Martin Hunt (hunt@cygnus.com)) |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| |
| #include "elf/s12z.h" |
| |
| /* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ |
| #define OCTETS_PER_BYTE(ABFD, SEC) 1 |
| |
| /* Relocation functions. */ |
| static reloc_howto_type *bfd_elf32_bfd_reloc_type_lookup |
| (bfd *, bfd_reloc_code_real_type); |
| static bool s12z_info_to_howto_rel |
| (bfd *, arelent *, Elf_Internal_Rela *); |
| |
| static bfd_reloc_status_type |
| opru18_reloc (bfd *abfd, arelent *reloc_entry, struct bfd_symbol *symbol, |
| void *data, asection *input_section ATTRIBUTE_UNUSED, |
| bfd *output ATTRIBUTE_UNUSED, char **msg ATTRIBUTE_UNUSED) |
| { |
| /* This reloc is used for 18 bit General Operand Addressing Postbyte in the |
| INST opru18 form. This is an 18 bit reloc, but the most significant bit |
| is shifted one place to the left of where it would normally be. See |
| Appendix A.4 of the S12Z reference manual. */ |
| |
| bfd_size_type octets = (reloc_entry->address |
| * OCTETS_PER_BYTE (abfd, input_section)); |
| bfd_vma result = bfd_get_24 (abfd, (unsigned char *) data + octets); |
| bfd_vma val = bfd_asymbol_value (symbol); |
| |
| /* Keep the wanted bits and discard the rest. */ |
| result &= 0xFA0000; |
| |
| val += symbol->section->output_section->vma; |
| val += symbol->section->output_offset; |
| |
| /* The lowest 17 bits are copied verbatim. */ |
| result |= val & 0x1FFFF; |
| |
| /* The 18th bit is put into the 19th position. */ |
| result |= (val & 0x020000) << 1; |
| |
| bfd_put_24 (abfd, result, (unsigned char *) data + octets); |
| |
| return bfd_reloc_ok; |
| } |
| |
| |
| static bfd_reloc_status_type |
| shift_addend_reloc (bfd *abfd, arelent *reloc_entry, struct bfd_symbol *symbol ATTRIBUTE_UNUSED, |
| void *data ATTRIBUTE_UNUSED, asection *input_section ATTRIBUTE_UNUSED, |
| bfd *output ATTRIBUTE_UNUSED, char **msg ATTRIBUTE_UNUSED) |
| { |
| /* This is a really peculiar reloc, which is done for compatibility |
| with the Freescale toolchain. |
| |
| That toolchain appears to (ab)use the lowest 15 bits of the addend for |
| the purpose of holding flags. The purpose of these flags are unknown. |
| So in this function, when writing the bfd we left shift the addend by |
| 15, and when reading we right shift it by 15 (discarding the lower bits). |
| |
| This allows the linker to work with object files generated by Freescale, |
| as well as by Gas. */ |
| |
| if (abfd->is_linker_input) |
| reloc_entry->addend >>= 15; |
| else |
| reloc_entry->addend <<= 15; |
| |
| return bfd_reloc_continue; |
| } |
| |
| #define USE_REL 0 |
| |
| static reloc_howto_type elf_s12z_howto_table[] = |
| { |
| /* This reloc does nothing. */ |
| HOWTO (R_S12Z_NONE, /* type */ |
| 0, /* rightshift */ |
| 0, /* size */ |
| 0, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_S12Z_NONE", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 24 bit absolute relocation emitted by the OPR mode operands */ |
| HOWTO (R_S12Z_OPR, /* type */ |
| 0, /* rightshift */ |
| 3, /* size */ |
| 24, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| shift_addend_reloc, |
| "R_S12Z_OPR", /* name */ |
| false, /* partial_inplace */ |
| 0x00ffffff, /* src_mask */ |
| 0x00ffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* The purpose of this reloc is not known */ |
| HOWTO (R_S12Z_UKNWN_2, /* type */ |
| 0, /* rightshift */ |
| 0, /* size */ |
| 0, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_dont,/* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_S12Z_UKNWN_2", /* name */ |
| false, /* partial_inplace */ |
| 0, /* src_mask */ |
| 0, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 15 bit PC-rel relocation */ |
| HOWTO (R_S12Z_PCREL_7_15, /* type */ |
| 0, /* rightshift */ |
| 2, /* size */ |
| 15, /* bitsize */ |
| true, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| shift_addend_reloc, |
| "R_S12Z_PCREL_7_15", /* name */ |
| false, /* partial_inplace */ |
| 0x00, /* src_mask */ |
| 0x007fff, /* dst_mask */ |
| true), /* pcrel_offset */ |
| |
| /* A 24 bit absolute relocation emitted by EXT24 mode operands */ |
| HOWTO (R_S12Z_EXT24, /* type */ |
| 0, /* rightshift */ |
| 3, /* size */ |
| 24, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_S12Z_EXT24", /* name */ |
| false, /* partial_inplace */ |
| 0x00000000, /* src_mask */ |
| 0x00ffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* An 18 bit absolute relocation */ |
| HOWTO (R_S12Z_EXT18, /* type */ |
| 0, /* rightshift */ |
| 3, /* size */ |
| 18, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| opru18_reloc, /* special_function */ |
| "R_S12Z_EXT18", /* name */ |
| false, /* partial_inplace */ |
| 0x00000000, /* src_mask */ |
| 0x0005ffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 32 bit absolute relocation. This kind of relocation is |
| schizophrenic - Although they appear in sections named .rela.debug.* |
| in some sections they behave as RELA relocs, but in others they have |
| an added of zero and behave as REL. |
| |
| It is not recommended that new code emits this reloc. It is here |
| only to support existing elf files generated by third party |
| applications. */ |
| |
| HOWTO (R_S12Z_CW32, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_S12Z_CW32", /* name */ |
| false, /* partial_inplace */ |
| 0xffffffff, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| |
| /* A 32 bit absolute relocation */ |
| HOWTO (R_S12Z_EXT32, /* type */ |
| 0, /* rightshift */ |
| 4, /* size */ |
| 32, /* bitsize */ |
| false, /* pc_relative */ |
| 0, /* bitpos */ |
| complain_overflow_bitfield, /* complain_on_overflow */ |
| bfd_elf_generic_reloc, /* special_function */ |
| "R_S12Z_EXT32", /* name */ |
| false, /* partial_inplace */ |
| 0x00000000, /* src_mask */ |
| 0xffffffff, /* dst_mask */ |
| false), /* pcrel_offset */ |
| }; |
| |
| /* Map BFD reloc types to S12Z ELF reloc types. */ |
| |
| struct s12z_reloc_map |
| { |
| bfd_reloc_code_real_type bfd_reloc_val; |
| unsigned char elf_reloc_val; |
| }; |
| |
| static const struct s12z_reloc_map s12z_reloc_map[] = |
| { |
| /* bfd reloc val */ /* elf reloc val */ |
| {BFD_RELOC_NONE, R_S12Z_NONE}, |
| {BFD_RELOC_32, R_S12Z_EXT32}, |
| {BFD_RELOC_24, R_S12Z_EXT24}, |
| {BFD_RELOC_16_PCREL, R_S12Z_PCREL_7_15}, |
| {BFD_RELOC_S12Z_OPR, R_S12Z_OPR} |
| }; |
| |
| static reloc_howto_type * |
| bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < sizeof (s12z_reloc_map) / sizeof (struct s12z_reloc_map); |
| i++) |
| { |
| if (s12z_reloc_map[i].bfd_reloc_val == code) |
| { |
| return &elf_s12z_howto_table[s12z_reloc_map[i].elf_reloc_val]; |
| } |
| } |
| |
| printf ("%s:%d Not found type %d\n", __FILE__, __LINE__, code); |
| |
| return NULL; |
| } |
| |
| static reloc_howto_type * |
| bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < (sizeof (elf_s12z_howto_table) |
| / sizeof (elf_s12z_howto_table[0])); |
| i++) |
| if (elf_s12z_howto_table[i].name != NULL |
| && strcasecmp (elf_s12z_howto_table[i].name, r_name) == 0) |
| return &elf_s12z_howto_table[i]; |
| |
| return NULL; |
| } |
| |
| /* Set the howto pointer for an S12Z ELF reloc. */ |
| |
| static bool |
| s12z_info_to_howto_rel (bfd *abfd, |
| arelent *cache_ptr, Elf_Internal_Rela *dst) |
| { |
| unsigned int r_type = ELF32_R_TYPE (dst->r_info); |
| |
| if (r_type >= (unsigned int) R_S12Z_max) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| cache_ptr->howto = &elf_s12z_howto_table[r_type]; |
| return true; |
| } |
| |
| static bool |
| s12z_elf_set_mach_from_flags (bfd *abfd) |
| { |
| bfd_default_set_arch_mach (abfd, bfd_arch_s12z, 0); |
| |
| return true; |
| } |
| |
| #define ELF_ARCH bfd_arch_s12z |
| #define ELF_MACHINE_CODE EM_S12Z |
| #define ELF_MAXPAGESIZE 0x1000 |
| |
| #define TARGET_BIG_SYM s12z_elf32_vec |
| #define TARGET_BIG_NAME "elf32-s12z" |
| |
| #define elf_info_to_howto NULL |
| #define elf_info_to_howto_rel s12z_info_to_howto_rel |
| #define elf_backend_object_p s12z_elf_set_mach_from_flags |
| |
| #include "elf32-target.h" |