/* Infineon XC16X-specific support for 16-bit ELF. Copyright (C) 2006-2022 Free Software Foundation, Inc. Contributed by KPIT Cummins Infosystems 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include "bfd.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf/xc16x.h" #include "dwarf2.h" #include "libiberty.h" static reloc_howto_type xc16x_elf_howto_table [] = { /* This reloc does nothing. */ HOWTO (R_XC16X_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_XC16X_NONE", /* name */ false, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ false), /* pcrel_offset */ /* An 8 bit absolute relocation. */ HOWTO (R_XC16X_ABS_8, /* type */ 0, /* rightshift */ 1, /* size */ 8, /* bitsize */ false, /* pc_relative */ 8, /* bitpos */ complain_overflow_bitfield, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_ABS_8", /* name */ true, /* partial_inplace */ 0x0000, /* src_mask */ 0x00ff, /* dst_mask */ false), /* pcrel_offset */ /* A 16 bit absolute relocation. */ HOWTO (R_XC16X_ABS_16, /* type */ 0, /* rightshift */ 2, /* size */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_ABS_16", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ HOWTO (R_XC16X_ABS_32, /* 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_XC16X_ABS_32", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0xffffffff, /* dst_mask */ false), /* pcrel_offset */ /* A PC relative 8 bit relocation. */ HOWTO (R_XC16X_8_PCREL, /* type */ 0, /* rightshift */ 1, /* size */ 8, /* bitsize */ true, /* pc_relative */ 8, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_8_PCREL", /* name */ false, /* partial_inplace */ 0x0000, /* src_mask */ 0x00ff, /* dst_mask */ true), /* pcrel_offset */ /* Relocation regarding page number. */ HOWTO (R_XC16X_PAG, /* type */ 0, /* rightshift */ 2, /* size */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_PAG", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ /* Relocation regarding page number. */ HOWTO (R_XC16X_POF, /* type */ 0, /* rightshift */ 2, /* size */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_POF", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ /* Relocation regarding segment number. */ HOWTO (R_XC16X_SEG, /* type */ 0, /* rightshift */ 2, /* size */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_SEG", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false), /* pcrel_offset */ /* Relocation regarding segment offset. */ HOWTO (R_XC16X_SOF, /* type */ 0, /* rightshift */ 2, /* size */ 16, /* bitsize */ false, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_XC16X_SOF", /* name */ true, /* partial_inplace */ 0x00000000, /* src_mask */ 0x0000ffff, /* dst_mask */ false) /* pcrel_offset */ }; /* Map BFD reloc types to XC16X ELF reloc types. */ struct xc16x_reloc_map { bfd_reloc_code_real_type bfd_reloc_val; unsigned int xc16x_reloc_val; }; static const struct xc16x_reloc_map xc16x_reloc_map [] = { { BFD_RELOC_NONE, R_XC16X_NONE }, { BFD_RELOC_8, R_XC16X_ABS_8 }, { BFD_RELOC_16, R_XC16X_ABS_16 }, { BFD_RELOC_32, R_XC16X_ABS_32 }, { BFD_RELOC_8_PCREL, R_XC16X_8_PCREL }, { BFD_RELOC_XC16X_PAG, R_XC16X_PAG}, { BFD_RELOC_XC16X_POF, R_XC16X_POF}, { BFD_RELOC_XC16X_SEG, R_XC16X_SEG}, { BFD_RELOC_XC16X_SOF, R_XC16X_SOF}, }; /* This function is used to search for correct relocation type from howto structure. */ static reloc_howto_type * xc16x_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { unsigned int i; for (i = ARRAY_SIZE (xc16x_reloc_map); --i;) if (xc16x_reloc_map [i].bfd_reloc_val == code) return & xc16x_elf_howto_table [xc16x_reloc_map[i].xc16x_reloc_val]; return NULL; } static reloc_howto_type * xc16x_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) { unsigned int i; for (i = 0; i < ARRAY_SIZE (xc16x_elf_howto_table); i++) if (xc16x_elf_howto_table[i].name != NULL && strcasecmp (xc16x_elf_howto_table[i].name, r_name) == 0) return &xc16x_elf_howto_table[i]; return NULL; } static reloc_howto_type * elf32_xc16x_rtype_to_howto (bfd *abfd ATTRIBUTE_UNUSED, unsigned r_type) { if (r_type < ARRAY_SIZE (xc16x_elf_howto_table)) return & xc16x_elf_howto_table[r_type]; return NULL; } /* For a particular operand this function is called to finalise the type of relocation. */ static bool elf32_xc16x_info_to_howto (bfd *abfd, arelent *bfd_reloc, Elf_Internal_Rela *elf_reloc) { unsigned int r; unsigned int i; r = ELF32_R_TYPE (elf_reloc->r_info); for (i = 0; i < ARRAY_SIZE (xc16x_elf_howto_table); i++) if (xc16x_elf_howto_table[i].type == r) { bfd_reloc->howto = &xc16x_elf_howto_table[i]; return true; } /* xgettext:c-format */ _bfd_error_handler (_("%pB: unsupported relocation type %#x"), abfd, r); bfd_set_error (bfd_error_bad_value); return false; } static bfd_reloc_status_type elf32_xc16x_final_link_relocate (unsigned long r_type, bfd *input_bfd, bfd *output_bfd ATTRIBUTE_UNUSED, asection *input_section ATTRIBUTE_UNUSED, bfd_byte *contents, bfd_vma offset, bfd_vma value, bfd_vma addend, struct bfd_link_info *info ATTRIBUTE_UNUSED, asection *sym_sec ATTRIBUTE_UNUSED, int is_local ATTRIBUTE_UNUSED) { bfd_byte *hit_data = contents + offset; bfd_vma val1; switch (r_type) { case R_XC16X_NONE: return bfd_reloc_ok; case R_XC16X_ABS_16: value += addend; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_XC16X_8_PCREL: bfd_put_8 (input_bfd, value, hit_data); return bfd_reloc_ok; /* Following case is to find page number from actual address for this divide value by 16k i.e. page size. */ case R_XC16X_PAG: value += addend; value /= 0x4000; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; /* Following case is to find page offset from actual address for this take modulo of value by 16k i.e. page size. */ case R_XC16X_POF: value += addend; value %= 0x4000; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; /* Following case is to find segment number from actual address for this divide value by 64k i.e. segment size. */ case R_XC16X_SEG: value += addend; value /= 0x10000; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; /* Following case is to find segment offset from actual address for this take modulo of value by 64k i.e. segment size. */ case R_XC16X_SOF: value += addend; value %= 0x10000; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_XC16X_ABS_32: if (!strstr (input_section->name,".debug")) { value += addend; val1 = value; value %= 0x4000; val1 /= 0x4000; val1 = val1 << 16; value += val1; bfd_put_32 (input_bfd, value, hit_data); } else { value += addend; bfd_put_32 (input_bfd, value, hit_data); } return bfd_reloc_ok; default: return bfd_reloc_notsupported; } } static int elf32_xc16x_relocate_section (bfd *output_bfd, struct bfd_link_info *info, bfd *input_bfd, asection *input_section, bfd_byte *contents, Elf_Internal_Rela *relocs, Elf_Internal_Sym *local_syms, asection **local_sections) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; Elf_Internal_Rela *rel, *relend; symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); rel = relocs; relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) { unsigned int r_type; unsigned long r_symndx; Elf_Internal_Sym *sym; asection *sec; struct elf_link_hash_entry *h; bfd_vma relocation; /* This is a final link. */ r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); h = NULL; sym = NULL; sec = NULL; if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; sec = local_sections[r_symndx]; relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); } else { bool unresolved_reloc, warned, ignored; RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, r_symndx, symtab_hdr, sym_hashes, h, sec, relocation, unresolved_reloc, warned, ignored); } if (sec != NULL && discarded_section (sec)) { /* For relocs against symbols from removed linkonce sections, or sections discarded by a linker script, we just want the section contents cleared. Avoid any special processing. */ reloc_howto_type *howto; howto = elf32_xc16x_rtype_to_howto (input_bfd, r_type); RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, rel, 1, relend, howto, 0, contents); } if (bfd_link_relocatable (info)) continue; elf32_xc16x_final_link_relocate (r_type, input_bfd, output_bfd, input_section, contents, rel->r_offset, relocation, rel->r_addend, info, sec, h == NULL); } return true; } static bool elf32_xc16x_final_write_processing (bfd *abfd) { unsigned long val; switch (bfd_get_mach (abfd)) { default: case bfd_mach_xc16x: val = 0x1000; break; case bfd_mach_xc16xl: val = 0x1001; break; case bfd_mach_xc16xs: val = 0x1002; break; } elf_elfheader (abfd)->e_flags |= val; return _bfd_elf_final_write_processing (abfd); } static unsigned long elf32_xc16x_mach (flagword flags) { switch (flags) { case 0x1000: default: return bfd_mach_xc16x; case 0x1001: return bfd_mach_xc16xl; case 0x1002: return bfd_mach_xc16xs; } } static bool elf32_xc16x_object_p (bfd *abfd) { bfd_default_set_arch_mach (abfd, bfd_arch_xc16x, elf32_xc16x_mach (elf_elfheader (abfd)->e_flags)); return true; } #define ELF_ARCH bfd_arch_xc16x #define ELF_MACHINE_CODE EM_XC16X #define ELF_MAXPAGESIZE 0x100 #define TARGET_LITTLE_SYM xc16x_elf32_vec #define TARGET_LITTLE_NAME "elf32-xc16x" #define elf_backend_final_write_processing elf32_xc16x_final_write_processing #define elf_backend_object_p elf32_xc16x_object_p #define elf_backend_can_gc_sections 1 #define bfd_elf32_bfd_reloc_type_lookup xc16x_reloc_type_lookup #define bfd_elf32_bfd_reloc_name_lookup xc16x_reloc_name_lookup #define elf_info_to_howto elf32_xc16x_info_to_howto #define elf_info_to_howto_rel elf32_xc16x_info_to_howto #define elf_backend_relocate_section elf32_xc16x_relocate_section #define elf_backend_rela_normal 1 #include "elf32-target.h"