| /* 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 */ |
| 3, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 0, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 2, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 0, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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 */ |
| 1, /* size (0 = byte, 1 = short, 2 = long) */ |
| 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" |