| /* Renesas RL78 specific support for 32-bit ELF. |
| Copyright (C) 2011-2021 Free Software Foundation, Inc. |
| |
| 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 "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/rl78.h" |
| #include "libiberty.h" |
| |
| #define valid_16bit_address(v) ((v) <= 0x0ffff || (v) >= 0xf0000) |
| |
| #define RL78REL(n,sz,bit,mask,shift,complain,pcrel) \ |
| HOWTO (R_RL78_##n, shift, sz, bit, pcrel, 0, complain_overflow_ ## complain, \ |
| bfd_elf_generic_reloc, "R_RL78_" #n, false, 0, mask, false) |
| |
| static bfd_reloc_status_type rl78_special_reloc (bfd *, arelent *, asymbol *, void *, |
| asection *, bfd *, char **); |
| |
| #define RL78_OP_REL(n,sz,bit,mask,shift,complain,pcrel) \ |
| HOWTO (R_RL78_##n, shift, sz, bit, pcrel, 0, complain_overflow_ ## complain, \ |
| rl78_special_reloc, "R_RL78_" #n, false, 0, mask, false) |
| |
| /* Note that the relocations around 0x7f are internal to this file; |
| feel free to move them as needed to avoid conflicts with published |
| relocation numbers. */ |
| |
| static reloc_howto_type rl78_elf_howto_table [] = |
| { |
| RL78REL (NONE, 3, 0, 0, 0, dont, false), |
| RL78REL (DIR32, 2, 32, 0xffffffff, 0, dont, false), |
| RL78REL (DIR24S, 2, 24, 0xffffff, 0, signed, false), |
| RL78REL (DIR16, 1, 16, 0xffff, 0, bitfield, false), |
| RL78REL (DIR16U, 1, 16, 0xffff, 0, unsigned, false), |
| RL78REL (DIR16S, 1, 16, 0xffff, 0, bitfield, false), |
| RL78REL (DIR8, 0, 8, 0xff, 0, dont, false), |
| RL78REL (DIR8U, 0, 8, 0xff, 0, unsigned, false), |
| RL78REL (DIR8S, 0, 8, 0xff, 0, bitfield, false), |
| RL78REL (DIR24S_PCREL, 2, 24, 0xffffff, 0, signed, true), |
| RL78REL (DIR16S_PCREL, 1, 16, 0xffff, 0, signed, true), |
| RL78REL (DIR8S_PCREL, 0, 8, 0xff, 0, signed, true), |
| RL78REL (DIR16UL, 1, 16, 0xffff, 2, unsigned, false), |
| RL78REL (DIR16UW, 1, 16, 0xffff, 1, unsigned, false), |
| RL78REL (DIR8UL, 0, 8, 0xff, 2, unsigned, false), |
| RL78REL (DIR8UW, 0, 8, 0xff, 1, unsigned, false), |
| RL78REL (DIR32_REV, 2, 32, 0xffffffff, 0, dont, false), |
| RL78REL (DIR16_REV, 1, 16, 0xffff, 0, bitfield, false), |
| RL78REL (DIR3U_PCREL, 0, 3, 0x7, 0, unsigned, true), |
| |
| EMPTY_HOWTO (0x13), |
| EMPTY_HOWTO (0x14), |
| EMPTY_HOWTO (0x15), |
| EMPTY_HOWTO (0x16), |
| EMPTY_HOWTO (0x17), |
| EMPTY_HOWTO (0x18), |
| EMPTY_HOWTO (0x19), |
| EMPTY_HOWTO (0x1a), |
| EMPTY_HOWTO (0x1b), |
| EMPTY_HOWTO (0x1c), |
| EMPTY_HOWTO (0x1d), |
| EMPTY_HOWTO (0x1e), |
| EMPTY_HOWTO (0x1f), |
| |
| EMPTY_HOWTO (0x20), |
| EMPTY_HOWTO (0x21), |
| EMPTY_HOWTO (0x22), |
| EMPTY_HOWTO (0x23), |
| EMPTY_HOWTO (0x24), |
| EMPTY_HOWTO (0x25), |
| EMPTY_HOWTO (0x26), |
| EMPTY_HOWTO (0x27), |
| EMPTY_HOWTO (0x28), |
| EMPTY_HOWTO (0x29), |
| EMPTY_HOWTO (0x2a), |
| EMPTY_HOWTO (0x2b), |
| EMPTY_HOWTO (0x2c), |
| |
| RL78REL (RH_RELAX, 3, 0, 0, 0, dont, false), |
| RL78REL (RH_SFR, 0, 8, 0xff, 0, unsigned, false), |
| RL78REL (RH_SADDR, 0, 8, 0xff, 0, unsigned, false), |
| |
| EMPTY_HOWTO (0x30), |
| EMPTY_HOWTO (0x31), |
| EMPTY_HOWTO (0x32), |
| EMPTY_HOWTO (0x33), |
| EMPTY_HOWTO (0x34), |
| EMPTY_HOWTO (0x35), |
| EMPTY_HOWTO (0x36), |
| EMPTY_HOWTO (0x37), |
| EMPTY_HOWTO (0x38), |
| EMPTY_HOWTO (0x39), |
| EMPTY_HOWTO (0x3a), |
| EMPTY_HOWTO (0x3b), |
| EMPTY_HOWTO (0x3c), |
| EMPTY_HOWTO (0x3d), |
| EMPTY_HOWTO (0x3e), |
| EMPTY_HOWTO (0x3f), |
| EMPTY_HOWTO (0x40), |
| |
| RL78_OP_REL (ABS32, 2, 32, 0xffffffff, 0, dont, false), |
| RL78_OP_REL (ABS24S, 2, 24, 0xffffff, 0, signed, false), |
| RL78_OP_REL (ABS16, 1, 16, 0xffff, 0, bitfield, false), |
| RL78_OP_REL (ABS16U, 1, 16, 0xffff, 0, unsigned, false), |
| RL78_OP_REL (ABS16S, 1, 16, 0xffff, 0, signed, false), |
| RL78_OP_REL (ABS8, 0, 8, 0xff, 0, bitfield, false), |
| RL78_OP_REL (ABS8U, 0, 8, 0xff, 0, unsigned, false), |
| RL78_OP_REL (ABS8S, 0, 8, 0xff, 0, signed, false), |
| RL78_OP_REL (ABS24S_PCREL, 2, 24, 0xffffff, 0, signed, true), |
| RL78_OP_REL (ABS16S_PCREL, 1, 16, 0xffff, 0, signed, true), |
| RL78_OP_REL (ABS8S_PCREL, 0, 8, 0xff, 0, signed, true), |
| RL78_OP_REL (ABS16UL, 1, 16, 0xffff, 0, unsigned, false), |
| RL78_OP_REL (ABS16UW, 1, 16, 0xffff, 0, unsigned, false), |
| RL78_OP_REL (ABS8UL, 0, 8, 0xff, 0, unsigned, false), |
| RL78_OP_REL (ABS8UW, 0, 8, 0xff, 0, unsigned, false), |
| RL78_OP_REL (ABS32_REV, 2, 32, 0xffffffff, 0, dont, false), |
| RL78_OP_REL (ABS16_REV, 1, 16, 0xffff, 0, bitfield, false), |
| |
| #define STACK_REL_P(x) ((x) <= R_RL78_ABS16_REV && (x) >= R_RL78_ABS32) |
| |
| EMPTY_HOWTO (0x52), |
| EMPTY_HOWTO (0x53), |
| EMPTY_HOWTO (0x54), |
| EMPTY_HOWTO (0x55), |
| EMPTY_HOWTO (0x56), |
| EMPTY_HOWTO (0x57), |
| EMPTY_HOWTO (0x58), |
| EMPTY_HOWTO (0x59), |
| EMPTY_HOWTO (0x5a), |
| EMPTY_HOWTO (0x5b), |
| EMPTY_HOWTO (0x5c), |
| EMPTY_HOWTO (0x5d), |
| EMPTY_HOWTO (0x5e), |
| EMPTY_HOWTO (0x5f), |
| EMPTY_HOWTO (0x60), |
| EMPTY_HOWTO (0x61), |
| EMPTY_HOWTO (0x62), |
| EMPTY_HOWTO (0x63), |
| EMPTY_HOWTO (0x64), |
| EMPTY_HOWTO (0x65), |
| EMPTY_HOWTO (0x66), |
| EMPTY_HOWTO (0x67), |
| EMPTY_HOWTO (0x68), |
| EMPTY_HOWTO (0x69), |
| EMPTY_HOWTO (0x6a), |
| EMPTY_HOWTO (0x6b), |
| EMPTY_HOWTO (0x6c), |
| EMPTY_HOWTO (0x6d), |
| EMPTY_HOWTO (0x6e), |
| EMPTY_HOWTO (0x6f), |
| EMPTY_HOWTO (0x70), |
| EMPTY_HOWTO (0x71), |
| EMPTY_HOWTO (0x72), |
| EMPTY_HOWTO (0x73), |
| EMPTY_HOWTO (0x74), |
| EMPTY_HOWTO (0x75), |
| EMPTY_HOWTO (0x76), |
| EMPTY_HOWTO (0x77), |
| |
| EMPTY_HOWTO (0x78), |
| EMPTY_HOWTO (0x79), |
| EMPTY_HOWTO (0x7a), |
| EMPTY_HOWTO (0x7b), |
| EMPTY_HOWTO (0x7c), |
| EMPTY_HOWTO (0x7d), |
| EMPTY_HOWTO (0x7e), |
| EMPTY_HOWTO (0x7f), |
| |
| RL78_OP_REL (SYM, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPneg, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPadd, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPsub, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPmul, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPdiv, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPshla, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPshra, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPsctsize, 3, 0, 0, 0, dont, false), |
| EMPTY_HOWTO (0x89), |
| EMPTY_HOWTO (0x8a), |
| EMPTY_HOWTO (0x8b), |
| EMPTY_HOWTO (0x8c), |
| RL78_OP_REL (OPscttop, 3, 0, 0, 0, dont, false), |
| EMPTY_HOWTO (0x8e), |
| EMPTY_HOWTO (0x8f), |
| RL78_OP_REL (OPand, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPor, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPxor, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPnot, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPmod, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPromtop, 3, 0, 0, 0, dont, false), |
| RL78_OP_REL (OPramtop, 3, 0, 0, 0, dont, false) |
| }; |
| |
| /* Map BFD reloc types to RL78 ELF reloc types. */ |
| |
| struct rl78_reloc_map |
| { |
| bfd_reloc_code_real_type bfd_reloc_val; |
| unsigned int rl78_reloc_val; |
| }; |
| |
| static const struct rl78_reloc_map rl78_reloc_map [] = |
| { |
| { BFD_RELOC_NONE, R_RL78_NONE }, |
| { BFD_RELOC_8, R_RL78_DIR8S }, |
| { BFD_RELOC_16, R_RL78_DIR16S }, |
| { BFD_RELOC_24, R_RL78_DIR24S }, |
| { BFD_RELOC_32, R_RL78_DIR32 }, |
| { BFD_RELOC_RL78_16_OP, R_RL78_DIR16 }, |
| { BFD_RELOC_RL78_DIR3U_PCREL, R_RL78_DIR3U_PCREL }, |
| { BFD_RELOC_8_PCREL, R_RL78_DIR8S_PCREL }, |
| { BFD_RELOC_16_PCREL, R_RL78_DIR16S_PCREL }, |
| { BFD_RELOC_24_PCREL, R_RL78_DIR24S_PCREL }, |
| { BFD_RELOC_RL78_8U, R_RL78_DIR8U }, |
| { BFD_RELOC_RL78_16U, R_RL78_DIR16U }, |
| { BFD_RELOC_RL78_SYM, R_RL78_SYM }, |
| { BFD_RELOC_RL78_OP_SUBTRACT, R_RL78_OPsub }, |
| { BFD_RELOC_RL78_OP_NEG, R_RL78_OPneg }, |
| { BFD_RELOC_RL78_OP_AND, R_RL78_OPand }, |
| { BFD_RELOC_RL78_OP_SHRA, R_RL78_OPshra }, |
| { BFD_RELOC_RL78_ABS8, R_RL78_ABS8 }, |
| { BFD_RELOC_RL78_ABS16, R_RL78_ABS16 }, |
| { BFD_RELOC_RL78_ABS16_REV, R_RL78_ABS16_REV }, |
| { BFD_RELOC_RL78_ABS32, R_RL78_ABS32 }, |
| { BFD_RELOC_RL78_ABS32_REV, R_RL78_ABS32_REV }, |
| { BFD_RELOC_RL78_ABS16UL, R_RL78_ABS16UL }, |
| { BFD_RELOC_RL78_ABS16UW, R_RL78_ABS16UW }, |
| { BFD_RELOC_RL78_ABS16U, R_RL78_ABS16U }, |
| { BFD_RELOC_RL78_SADDR, R_RL78_RH_SADDR }, |
| { BFD_RELOC_RL78_RELAX, R_RL78_RH_RELAX } |
| }; |
| |
| static reloc_howto_type * |
| rl78_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| unsigned int i; |
| |
| if (code == BFD_RELOC_RL78_32_OP) |
| return rl78_elf_howto_table + R_RL78_DIR32; |
| |
| for (i = ARRAY_SIZE (rl78_reloc_map); i--;) |
| if (rl78_reloc_map [i].bfd_reloc_val == code) |
| return rl78_elf_howto_table + rl78_reloc_map[i].rl78_reloc_val; |
| |
| return NULL; |
| } |
| |
| static reloc_howto_type * |
| rl78_reloc_name_lookup (bfd * abfd ATTRIBUTE_UNUSED, const char * r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < ARRAY_SIZE (rl78_elf_howto_table); i++) |
| if (rl78_elf_howto_table[i].name != NULL |
| && strcasecmp (rl78_elf_howto_table[i].name, r_name) == 0) |
| return rl78_elf_howto_table + i; |
| |
| return NULL; |
| } |
| |
| /* Set the howto pointer for an RL78 ELF reloc. */ |
| |
| static bool |
| rl78_info_to_howto_rela (bfd * abfd, |
| arelent * cache_ptr, |
| Elf_Internal_Rela * dst) |
| { |
| unsigned int r_type; |
| |
| r_type = ELF32_R_TYPE (dst->r_info); |
| if (r_type >= (unsigned int) R_RL78_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 = rl78_elf_howto_table + r_type; |
| return true; |
| } |
| |
| static bfd_vma |
| get_symbol_value (const char * name, |
| struct bfd_link_info * info, |
| bfd * input_bfd, |
| asection * input_section, |
| int offset) |
| { |
| struct bfd_link_hash_entry * h; |
| |
| if (info == NULL) |
| return 0; |
| |
| h = bfd_link_hash_lookup (info->hash, name, false, false, true); |
| |
| if (h == NULL |
| || (h->type != bfd_link_hash_defined |
| && h->type != bfd_link_hash_defweak)) |
| { |
| (*info->callbacks->undefined_symbol) |
| (info, name, input_bfd, input_section, offset, true); |
| return 0; |
| } |
| |
| return (h->u.def.value |
| + h->u.def.section->output_section->vma |
| + h->u.def.section->output_offset); |
| } |
| |
| static bfd_vma |
| get_romstart (struct bfd_link_info * info, |
| bfd * abfd, |
| asection * sec, |
| int offset) |
| { |
| static bool cached = false; |
| static bfd_vma cached_value = 0; |
| |
| if (!cached) |
| { |
| cached_value = get_symbol_value ("_start", info, abfd, sec, offset); |
| cached = true; |
| } |
| return cached_value; |
| } |
| |
| static bfd_vma |
| get_ramstart (struct bfd_link_info * info, |
| bfd * abfd, |
| asection * sec, |
| int offset) |
| { |
| static bool cached = false; |
| static bfd_vma cached_value = 0; |
| |
| if (!cached) |
| { |
| cached_value = get_symbol_value ("__datastart", info, abfd, sec, offset); |
| cached = true; |
| } |
| return cached_value; |
| } |
| |
| #define NUM_STACK_ENTRIES 16 |
| static int32_t rl78_stack [ NUM_STACK_ENTRIES ]; |
| static unsigned int rl78_stack_top; |
| |
| static inline void |
| rl78_stack_push (bfd_vma val, bfd_reloc_status_type *r) |
| { |
| if (rl78_stack_top < NUM_STACK_ENTRIES) |
| rl78_stack[rl78_stack_top++] = val; |
| else |
| *r = bfd_reloc_dangerous; |
| } |
| |
| static inline bfd_vma |
| rl78_stack_pop (bfd_reloc_status_type *r) |
| { |
| if (rl78_stack_top > 0) |
| return rl78_stack[-- rl78_stack_top]; |
| else |
| *r = bfd_reloc_dangerous; |
| return 0; |
| } |
| |
| /* Special handling for RL78 complex relocs. Returns the |
| value of the reloc, or 0 for relocs which do not generate |
| a result. SYMVAL is the value of the symbol for relocs |
| which use a symbolic argument. */ |
| |
| static bfd_vma |
| rl78_compute_complex_reloc (unsigned long r_type, |
| bfd_vma symval, |
| asection *input_section, |
| bfd_reloc_status_type *r, |
| char **error_message) |
| { |
| int32_t tmp1, tmp2; |
| bfd_vma relocation = 0; |
| bfd_reloc_status_type stat = bfd_reloc_ok; |
| |
| switch (r_type) |
| { |
| default: |
| stat = bfd_reloc_notsupported; |
| break; |
| |
| case R_RL78_ABS24S_PCREL: |
| case R_RL78_ABS16S_PCREL: |
| case R_RL78_ABS8S_PCREL: |
| relocation = rl78_stack_pop (&stat); |
| relocation -= input_section->output_section->vma + input_section->output_offset; |
| break; |
| |
| case R_RL78_ABS32: |
| case R_RL78_ABS32_REV: |
| case R_RL78_ABS16: |
| case R_RL78_ABS16_REV: |
| case R_RL78_ABS16S: |
| case R_RL78_ABS16U: |
| case R_RL78_ABS8: |
| case R_RL78_ABS8U: |
| case R_RL78_ABS8S: |
| relocation = rl78_stack_pop (&stat); |
| break; |
| |
| case R_RL78_ABS16UL: |
| case R_RL78_ABS8UL: |
| relocation = rl78_stack_pop (&stat) >> 2; |
| break;; |
| |
| case R_RL78_ABS16UW: |
| case R_RL78_ABS8UW: |
| relocation = rl78_stack_pop (&stat) >> 1; |
| break; |
| |
| /* The rest of the relocs compute values and then push them onto the stack. */ |
| case R_RL78_OPramtop: |
| case R_RL78_OPromtop: |
| case R_RL78_SYM: |
| rl78_stack_push (symval, &stat); |
| break; |
| |
| case R_RL78_OPneg: |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 = - tmp1; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPadd: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 += tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPsub: |
| /* For the expression "A - B", the assembler pushes A, |
| then B, then OPSUB. So the first op we pop is B, not A. */ |
| tmp2 = rl78_stack_pop (&stat); /* B */ |
| tmp1 = rl78_stack_pop (&stat); /* A */ |
| tmp1 -= tmp2; /* A - B */ |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPmul: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 *= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPdiv: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| if (tmp2 != 0) |
| tmp1 /= tmp2; |
| else |
| { |
| tmp1 = 0; |
| stat = bfd_reloc_overflow; |
| } |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPshla: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 <<= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPshra: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 >>= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPsctsize: |
| rl78_stack_push (input_section->size, &stat); |
| break; |
| |
| case R_RL78_OPscttop: |
| rl78_stack_push (input_section->output_section->vma, &stat); |
| break; |
| |
| case R_RL78_OPand: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 &= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPor: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 |= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPxor: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 ^= tmp2; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPnot: |
| tmp1 = rl78_stack_pop (&stat); |
| tmp1 = ~ tmp1; |
| rl78_stack_push (tmp1, &stat); |
| break; |
| |
| case R_RL78_OPmod: |
| tmp2 = rl78_stack_pop (&stat); |
| tmp1 = rl78_stack_pop (&stat); |
| if (tmp2 != 0) |
| tmp1 %= tmp2; |
| else |
| { |
| tmp1 = 0; |
| stat = bfd_reloc_overflow; |
| } |
| rl78_stack_push (tmp1, &stat); |
| break; |
| } |
| |
| if (r) |
| { |
| if (stat == bfd_reloc_dangerous) |
| *error_message = (_("RL78 reloc stack overflow/underflow")); |
| else if (stat == bfd_reloc_overflow) |
| { |
| stat = bfd_reloc_dangerous; |
| *error_message = (_("RL78 reloc divide by zero")); |
| } |
| *r = stat; |
| } |
| return relocation; |
| } |
| |
| /* Check whether RELOCATION overflows a relocation field described by |
| HOWTO. */ |
| |
| static bfd_reloc_status_type |
| check_overflow (reloc_howto_type *howto, bfd_vma relocation) |
| { |
| switch (howto->complain_on_overflow) |
| { |
| case complain_overflow_dont: |
| break; |
| |
| case complain_overflow_bitfield: |
| if ((bfd_signed_vma) relocation < -(1LL << (howto->bitsize - 1)) |
| || (bfd_signed_vma) relocation >= 1LL << howto->bitsize) |
| return bfd_reloc_overflow; |
| break; |
| |
| case complain_overflow_signed: |
| if ((bfd_signed_vma) relocation < -(1LL << (howto->bitsize - 1)) |
| || (bfd_signed_vma) relocation >= 1LL << (howto->bitsize - 1)) |
| return bfd_reloc_overflow; |
| break; |
| |
| case complain_overflow_unsigned: |
| if (relocation >= 1ULL << howto->bitsize) |
| return bfd_reloc_overflow; |
| break; |
| } |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| rl78_special_reloc (bfd * input_bfd, |
| arelent * reloc, |
| asymbol * symbol, |
| void * data, |
| asection * input_section, |
| bfd * output_bfd ATTRIBUTE_UNUSED, |
| char ** error_message) |
| { |
| bfd_reloc_status_type r = bfd_reloc_ok; |
| bfd_vma relocation = 0; |
| unsigned long r_type = reloc->howto->type; |
| bfd_byte * contents = data; |
| |
| /* If necessary, compute the symbolic value of the relocation. */ |
| switch (r_type) |
| { |
| case R_RL78_SYM: |
| relocation = (symbol->value |
| + symbol->section->output_section->vma |
| + symbol->section->output_offset |
| + reloc->addend); |
| break; |
| |
| case R_RL78_OPromtop: |
| relocation = get_romstart (NULL, input_bfd, input_section, |
| reloc->address); |
| break; |
| |
| case R_RL78_OPramtop: |
| relocation = get_ramstart (NULL, input_bfd, input_section, |
| reloc->address); |
| break; |
| } |
| |
| /* Get the value of the relocation. */ |
| relocation = rl78_compute_complex_reloc (r_type, relocation, input_section, |
| &r, error_message); |
| |
| if (STACK_REL_P (r_type)) |
| { |
| bfd_size_type limit; |
| unsigned int nbytes; |
| |
| if (r == bfd_reloc_ok) |
| r = check_overflow (reloc->howto, relocation); |
| |
| if (r_type == R_RL78_ABS16_REV) |
| relocation = ((relocation & 0xff) << 8) | ((relocation >> 8) & 0xff); |
| else if (r_type == R_RL78_ABS32_REV) |
| relocation = (((relocation & 0xff) << 24) |
| | ((relocation & 0xff00) << 8) |
| | ((relocation >> 8) & 0xff00) |
| | ((relocation >> 24) & 0xff)); |
| |
| limit = bfd_get_section_limit_octets (input_bfd, input_section); |
| nbytes = reloc->howto->bitsize / 8; |
| if (reloc->address < limit |
| && nbytes <= limit - reloc->address) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < nbytes; i++) |
| { |
| contents[reloc->address + i] = relocation; |
| relocation >>= 8; |
| } |
| } |
| else |
| r = bfd_reloc_outofrange; |
| } |
| |
| return r; |
| } |
| |
| #define OP(i) (contents[rel->r_offset + (i)]) |
| |
| /* Relocate an RL78 ELF section. |
| There is some attempt to make this function usable for many architectures, |
| both USE_REL and USE_RELA ['twould be nice if such a critter existed], |
| if only to serve as a learning tool. |
| |
| The RELOCATE_SECTION function is called by the new ELF backend linker |
| to handle the relocations for a section. |
| |
| The relocs are always passed as Rela structures; if the section |
| actually uses Rel structures, the r_addend field will always be |
| zero. |
| |
| This function is responsible for adjusting the section contents as |
| necessary, and (if using Rela relocs and generating a relocatable |
| output file) adjusting the reloc addend as necessary. |
| |
| This function does not have to worry about setting the reloc |
| address or the reloc symbol index. |
| |
| LOCAL_SYMS is a pointer to the swapped in local symbols. |
| |
| LOCAL_SECTIONS is an array giving the section in the input file |
| corresponding to the st_shndx field of each local symbol. |
| |
| The global hash table entry for the global symbols can be found |
| via elf_sym_hashes (input_bfd). |
| |
| When generating relocatable output, this function must handle |
| STB_LOCAL/STT_SECTION symbols specially. The output symbol is |
| going to be the section symbol corresponding to the output |
| section, which means that the addend must be adjusted |
| accordingly. */ |
| |
| static int |
| rl78_elf_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; |
| Elf_Internal_Rela * relend; |
| asection *splt; |
| bool ret; |
| |
| symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (input_bfd); |
| relend = relocs + input_section->reloc_count; |
| |
| splt = elf_hash_table (info)->splt; |
| ret = true; |
| for (rel = relocs; rel < relend; rel ++) |
| { |
| reloc_howto_type *howto; |
| unsigned long r_symndx; |
| Elf_Internal_Sym *sym; |
| asection *sec; |
| struct elf_link_hash_entry *h; |
| bfd_vma relocation; |
| bfd_reloc_status_type r; |
| const char *name = NULL; |
| bool unresolved_reloc = true; |
| int r_type; |
| char *error_message; |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| |
| howto = rl78_elf_howto_table + ELF32_R_TYPE (rel->r_info); |
| h = NULL; |
| sym = NULL; |
| sec = NULL; |
| relocation = 0; |
| |
| 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); |
| |
| name = bfd_elf_string_from_elf_section |
| (input_bfd, symtab_hdr->sh_link, sym->st_name); |
| name = sym->st_name == 0 ? bfd_section_name (sec) : name; |
| } |
| else |
| { |
| bool warned ATTRIBUTE_UNUSED; |
| bool ignored ATTRIBUTE_UNUSED; |
| |
| RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, |
| r_symndx, symtab_hdr, sym_hashes, h, |
| sec, relocation, unresolved_reloc, |
| warned, ignored); |
| |
| name = h->root.root.string; |
| } |
| |
| if (sec != NULL && discarded_section (sec)) |
| RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, |
| rel, 1, relend, howto, 0, contents); |
| |
| if (bfd_link_relocatable (info)) |
| { |
| /* This is a relocatable link. We don't have to change |
| anything, unless the reloc is against a section symbol, |
| in which case we have to adjust according to where the |
| section symbol winds up in the output section. */ |
| if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) |
| rel->r_addend += sec->output_offset; |
| continue; |
| } |
| |
| switch (ELF32_R_TYPE (rel->r_info)) |
| { |
| case R_RL78_DIR16S: |
| { |
| bfd_vma *plt_offset; |
| |
| if (h != NULL) |
| plt_offset = &h->plt.offset; |
| else |
| plt_offset = elf_local_got_offsets (input_bfd) + r_symndx; |
| |
| if (! valid_16bit_address (relocation)) |
| { |
| /* If this is the first time we've processed this symbol, |
| fill in the plt entry with the correct symbol address. */ |
| if ((*plt_offset & 1) == 0) |
| { |
| unsigned int x; |
| |
| x = 0x000000ec; /* br !!abs24 */ |
| x |= (relocation << 8) & 0xffffff00; |
| bfd_put_32 (input_bfd, x, splt->contents + *plt_offset); |
| *plt_offset |= 1; |
| } |
| |
| relocation = (splt->output_section->vma |
| + splt->output_offset |
| + (*plt_offset & -2)); |
| if (name) |
| { |
| char *newname = bfd_malloc (strlen(name)+5); |
| strcpy (newname, name); |
| strcat(newname, ".plt"); |
| _bfd_generic_link_add_one_symbol (info, |
| input_bfd, |
| newname, |
| BSF_FUNCTION | BSF_WEAK, |
| splt, |
| (*plt_offset & -2), |
| 0, |
| 1, |
| 0, |
| 0); |
| } |
| } |
| } |
| break; |
| } |
| |
| if (h != NULL && h->root.type == bfd_link_hash_undefweak) |
| /* If the symbol is undefined and weak |
| then the relocation resolves to zero. */ |
| relocation = 0; |
| else |
| { |
| if (howto->pc_relative) |
| { |
| relocation -= (input_section->output_section->vma |
| + input_section->output_offset |
| + rel->r_offset); |
| relocation -= bfd_get_reloc_size (howto); |
| } |
| |
| relocation += rel->r_addend; |
| } |
| |
| r = bfd_reloc_ok; |
| if (howto->bitsize != 0 |
| && (rel->r_offset >= input_section->size |
| || ((howto->bitsize + 7u) / 8 |
| > input_section->size - rel->r_offset))) |
| r = bfd_reloc_outofrange; |
| else |
| switch (r_type) |
| { |
| case R_RL78_NONE: |
| break; |
| |
| case R_RL78_RH_RELAX: |
| break; |
| |
| case R_RL78_DIR8S_PCREL: |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_DIR8S: |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_DIR8U: |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_DIR16S_PCREL: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_DIR16S: |
| if ((relocation & 0xf0000) == 0xf0000) |
| relocation &= 0xffff; |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_DIR16U: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_DIR16: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_DIR16_REV: |
| OP (1) = relocation; |
| OP (0) = relocation >> 8; |
| break; |
| |
| case R_RL78_DIR3U_PCREL: |
| OP (0) &= 0xf8; |
| OP (0) |= relocation & 0x07; |
| /* Map [3, 10] to [0, 7]. The code below using howto |
| bitsize will check for unsigned overflow. */ |
| relocation -= 3; |
| break; |
| |
| case R_RL78_DIR24S_PCREL: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| OP (2) = relocation >> 16; |
| break; |
| |
| case R_RL78_DIR24S: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| OP (2) = relocation >> 16; |
| break; |
| |
| case R_RL78_DIR32: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| OP (2) = relocation >> 16; |
| OP (3) = relocation >> 24; |
| break; |
| |
| case R_RL78_DIR32_REV: |
| OP (3) = relocation; |
| OP (2) = relocation >> 8; |
| OP (1) = relocation >> 16; |
| OP (0) = relocation >> 24; |
| break; |
| |
| case R_RL78_RH_SFR: |
| relocation -= 0xfff00; |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_RH_SADDR: |
| relocation -= 0xffe20; |
| OP (0) = relocation; |
| break; |
| |
| /* Complex reloc handling: */ |
| case R_RL78_ABS32: |
| case R_RL78_ABS32_REV: |
| case R_RL78_ABS24S_PCREL: |
| case R_RL78_ABS24S: |
| case R_RL78_ABS16: |
| case R_RL78_ABS16_REV: |
| case R_RL78_ABS16S_PCREL: |
| case R_RL78_ABS16S: |
| case R_RL78_ABS16U: |
| case R_RL78_ABS16UL: |
| case R_RL78_ABS16UW: |
| case R_RL78_ABS8: |
| case R_RL78_ABS8U: |
| case R_RL78_ABS8UL: |
| case R_RL78_ABS8UW: |
| case R_RL78_ABS8S_PCREL: |
| case R_RL78_ABS8S: |
| case R_RL78_OPneg: |
| case R_RL78_OPadd: |
| case R_RL78_OPsub: |
| case R_RL78_OPmul: |
| case R_RL78_OPdiv: |
| case R_RL78_OPshla: |
| case R_RL78_OPshra: |
| case R_RL78_OPsctsize: |
| case R_RL78_OPscttop: |
| case R_RL78_OPand: |
| case R_RL78_OPor: |
| case R_RL78_OPxor: |
| case R_RL78_OPnot: |
| case R_RL78_OPmod: |
| relocation = rl78_compute_complex_reloc (r_type, 0, input_section, |
| &r, &error_message); |
| |
| switch (r_type) |
| { |
| case R_RL78_ABS32: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| OP (2) = relocation >> 16; |
| OP (3) = relocation >> 24; |
| break; |
| |
| case R_RL78_ABS32_REV: |
| OP (3) = relocation; |
| OP (2) = relocation >> 8; |
| OP (1) = relocation >> 16; |
| OP (0) = relocation >> 24; |
| break; |
| |
| case R_RL78_ABS24S_PCREL: |
| case R_RL78_ABS24S: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| OP (2) = relocation >> 16; |
| break; |
| |
| case R_RL78_ABS16: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_ABS16_REV: |
| OP (1) = relocation; |
| OP (0) = relocation >> 8; |
| break; |
| |
| case R_RL78_ABS16S_PCREL: |
| case R_RL78_ABS16S: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_ABS16U: |
| case R_RL78_ABS16UL: |
| case R_RL78_ABS16UW: |
| OP (0) = relocation; |
| OP (1) = relocation >> 8; |
| break; |
| |
| case R_RL78_ABS8: |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_ABS8U: |
| case R_RL78_ABS8UL: |
| case R_RL78_ABS8UW: |
| OP (0) = relocation; |
| break; |
| |
| case R_RL78_ABS8S_PCREL: |
| case R_RL78_ABS8S: |
| OP (0) = relocation; |
| break; |
| |
| default: |
| break; |
| } |
| break; |
| |
| case R_RL78_SYM: |
| if (r_symndx < symtab_hdr->sh_info) |
| relocation = sec->output_section->vma + sec->output_offset |
| + sym->st_value + rel->r_addend; |
| else if (h != NULL |
| && (h->root.type == bfd_link_hash_defined |
| || h->root.type == bfd_link_hash_defweak)) |
| relocation = h->root.u.def.value |
| + sec->output_section->vma |
| + sec->output_offset |
| + rel->r_addend; |
| else |
| { |
| relocation = 0; |
| if (h->root.type != bfd_link_hash_undefweak) |
| _bfd_error_handler |
| (_("warning: RL78_SYM reloc with an unknown symbol")); |
| } |
| (void) rl78_compute_complex_reloc (r_type, relocation, input_section, |
| &r, &error_message); |
| break; |
| |
| case R_RL78_OPromtop: |
| relocation = get_romstart (info, input_bfd, input_section, |
| rel->r_offset); |
| (void) rl78_compute_complex_reloc (r_type, relocation, input_section, |
| &r, &error_message); |
| break; |
| |
| case R_RL78_OPramtop: |
| relocation = get_ramstart (info, input_bfd, input_section, |
| rel->r_offset); |
| (void) rl78_compute_complex_reloc (r_type, relocation, input_section, |
| &r, &error_message); |
| break; |
| |
| default: |
| r = bfd_reloc_notsupported; |
| break; |
| } |
| |
| if (r == bfd_reloc_ok) |
| r = check_overflow (howto, relocation); |
| |
| if (r != bfd_reloc_ok) |
| { |
| switch (r) |
| { |
| case bfd_reloc_overflow: |
| (*info->callbacks->reloc_overflow) |
| (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0, |
| input_bfd, input_section, rel->r_offset); |
| break; |
| |
| case bfd_reloc_undefined: |
| (*info->callbacks->undefined_symbol) |
| (info, name, input_bfd, input_section, rel->r_offset, true); |
| break; |
| |
| case bfd_reloc_outofrange: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%H: %s out of range\n"), |
| input_bfd, input_section, rel->r_offset, howto->name); |
| break; |
| |
| case bfd_reloc_notsupported: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%H: relocation type %u is not supported\n"), |
| input_bfd, input_section, rel->r_offset, r_type); |
| break; |
| |
| case bfd_reloc_dangerous: |
| (*info->callbacks->reloc_dangerous) |
| (info, error_message, input_bfd, input_section, rel->r_offset); |
| break; |
| |
| default: |
| /* xgettext:c-format */ |
| (*info->callbacks->einfo) |
| (_("%H: relocation %s returns an unrecognized value %x\n"), |
| input_bfd, input_section, rel->r_offset, howto->name, r); |
| break; |
| } |
| ret = false; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /* Function to set the ELF flag bits. */ |
| |
| static bool |
| rl78_elf_set_private_flags (bfd * abfd, flagword flags) |
| { |
| elf_elfheader (abfd)->e_flags = flags; |
| elf_flags_init (abfd) = true; |
| return true; |
| } |
| |
| static bool no_warn_mismatch = false; |
| |
| void bfd_elf32_rl78_set_target_flags (bool); |
| |
| void |
| bfd_elf32_rl78_set_target_flags (bool user_no_warn_mismatch) |
| { |
| no_warn_mismatch = user_no_warn_mismatch; |
| } |
| |
| static const char * |
| rl78_cpu_name (flagword flags) |
| { |
| switch (flags & E_FLAG_RL78_CPU_MASK) |
| { |
| default: return ""; |
| case E_FLAG_RL78_G10: return "G10"; |
| case E_FLAG_RL78_G13: return "G13"; |
| case E_FLAG_RL78_G14: return "G14"; |
| } |
| } |
| |
| /* Merge backend specific data from an object file to the output |
| object file when linking. */ |
| |
| static bool |
| rl78_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) |
| { |
| bfd *obfd = info->output_bfd; |
| flagword new_flags; |
| flagword old_flags; |
| bool error = false; |
| |
| new_flags = elf_elfheader (ibfd)->e_flags; |
| old_flags = elf_elfheader (obfd)->e_flags; |
| |
| if (!elf_flags_init (obfd)) |
| { |
| /* First call, no flags set. */ |
| elf_flags_init (obfd) = true; |
| elf_elfheader (obfd)->e_flags = new_flags; |
| } |
| else if (old_flags != new_flags) |
| { |
| flagword changed_flags = old_flags ^ new_flags; |
| |
| if (changed_flags & E_FLAG_RL78_CPU_MASK) |
| { |
| flagword out_cpu = old_flags & E_FLAG_RL78_CPU_MASK; |
| flagword in_cpu = new_flags & E_FLAG_RL78_CPU_MASK; |
| |
| if (in_cpu == E_FLAG_RL78_ANY_CPU || in_cpu == out_cpu) |
| /* It does not matter what new_cpu may have. */; |
| else if (out_cpu == E_FLAG_RL78_ANY_CPU) |
| { |
| if (in_cpu == E_FLAG_RL78_G10) |
| { |
| /* G10 files can only be linked with other G10 files. |
| If the output is set to "any" this means that it is |
| a G14 file that does not use hardware multiply/divide, |
| but that is still incompatible with the G10 ABI. */ |
| error = true; |
| |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("RL78 ABI conflict: G10 file %pB cannot be linked" |
| " with %s file %pB"), |
| ibfd, rl78_cpu_name (out_cpu), obfd); |
| } |
| else |
| { |
| old_flags &= ~ E_FLAG_RL78_CPU_MASK; |
| old_flags |= in_cpu; |
| elf_elfheader (obfd)->e_flags = old_flags; |
| } |
| } |
| else |
| { |
| error = true; |
| |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("RL78 ABI conflict: cannot link %s file %pB with %s file %pB"), |
| rl78_cpu_name (in_cpu), ibfd, |
| rl78_cpu_name (out_cpu), obfd); |
| } |
| } |
| |
| if (changed_flags & E_FLAG_RL78_64BIT_DOUBLES) |
| { |
| _bfd_error_handler |
| (_("RL78 merge conflict: cannot link 32-bit and 64-bit objects together")); |
| |
| if (old_flags & E_FLAG_RL78_64BIT_DOUBLES) |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("- %pB is 64-bit, %pB is not"), |
| obfd, ibfd); |
| else |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("- %pB is 64-bit, %pB is not"), |
| ibfd, obfd); |
| error = true; |
| } |
| } |
| |
| return !error; |
| } |
| |
| static bool |
| rl78_elf_print_private_bfd_data (bfd * abfd, void * ptr) |
| { |
| FILE * file = (FILE *) ptr; |
| flagword flags; |
| |
| BFD_ASSERT (abfd != NULL && ptr != NULL); |
| |
| /* Print normal ELF private data. */ |
| _bfd_elf_print_private_bfd_data (abfd, ptr); |
| |
| flags = elf_elfheader (abfd)->e_flags; |
| fprintf (file, _("private flags = 0x%lx:"), (long) flags); |
| |
| if (flags & E_FLAG_RL78_CPU_MASK) |
| fprintf (file, " [%s]", rl78_cpu_name (flags)); |
| |
| if (flags & E_FLAG_RL78_64BIT_DOUBLES) |
| fprintf (file, _(" [64-bit doubles]")); |
| |
| fputc ('\n', file); |
| return true; |
| } |
| |
| /* Return the MACH for an e_flags value. */ |
| |
| static int |
| elf32_rl78_machine (bfd * abfd ATTRIBUTE_UNUSED) |
| { |
| return bfd_mach_rl78; |
| } |
| |
| static bool |
| rl78_elf_object_p (bfd * abfd) |
| { |
| bfd_default_set_arch_mach (abfd, bfd_arch_rl78, |
| elf32_rl78_machine (abfd)); |
| return true; |
| } |
| |
| /* support PLT for 16-bit references to 24-bit functions. */ |
| |
| /* We support 16-bit pointers to code above 64k by generating a thunk |
| below 64k containing a JMP instruction to the final address. */ |
| |
| static bool |
| rl78_elf_check_relocs |
| (bfd * abfd, |
| struct bfd_link_info * info, |
| asection * sec, |
| const Elf_Internal_Rela * relocs) |
| { |
| Elf_Internal_Shdr * symtab_hdr; |
| struct elf_link_hash_entry ** sym_hashes; |
| const Elf_Internal_Rela * rel; |
| const Elf_Internal_Rela * rel_end; |
| bfd_vma *local_plt_offsets; |
| asection *splt; |
| bfd *dynobj; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| sym_hashes = elf_sym_hashes (abfd); |
| local_plt_offsets = elf_local_got_offsets (abfd); |
| dynobj = elf_hash_table(info)->dynobj; |
| |
| rel_end = relocs + sec->reloc_count; |
| for (rel = relocs; rel < rel_end; rel++) |
| { |
| struct elf_link_hash_entry *h; |
| unsigned long r_symndx; |
| bfd_vma *offset; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| if (r_symndx < symtab_hdr->sh_info) |
| h = NULL; |
| else |
| { |
| h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
| while (h->root.type == bfd_link_hash_indirect |
| || h->root.type == bfd_link_hash_warning) |
| h = (struct elf_link_hash_entry *) h->root.u.i.link; |
| } |
| |
| switch (ELF32_R_TYPE (rel->r_info)) |
| { |
| /* This relocation describes a 16-bit pointer to a function. |
| We may need to allocate a thunk in low memory; reserve memory |
| for it now. */ |
| case R_RL78_DIR16S: |
| if (dynobj == NULL) |
| elf_hash_table (info)->dynobj = dynobj = abfd; |
| splt = elf_hash_table (info)->splt; |
| if (splt == NULL) |
| { |
| flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED |
| | SEC_READONLY | SEC_CODE); |
| splt = bfd_make_section_anyway_with_flags (dynobj, ".plt", |
| flags); |
| elf_hash_table (info)->splt = splt; |
| if (splt == NULL |
| || !bfd_set_section_alignment (splt, 1)) |
| return false; |
| } |
| |
| if (h != NULL) |
| offset = &h->plt.offset; |
| else |
| { |
| if (local_plt_offsets == NULL) |
| { |
| size_t size; |
| unsigned int i; |
| |
| size = symtab_hdr->sh_info * sizeof (bfd_vma); |
| local_plt_offsets = (bfd_vma *) bfd_alloc (abfd, size); |
| if (local_plt_offsets == NULL) |
| return false; |
| elf_local_got_offsets (abfd) = local_plt_offsets; |
| |
| for (i = 0; i < symtab_hdr->sh_info; i++) |
| local_plt_offsets[i] = (bfd_vma) -1; |
| } |
| offset = &local_plt_offsets[r_symndx]; |
| } |
| |
| if (*offset == (bfd_vma) -1) |
| { |
| *offset = splt->size; |
| splt->size += 4; |
| } |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* This must exist if dynobj is ever set. */ |
| |
| static bool |
| rl78_elf_finish_dynamic_sections (bfd *abfd ATTRIBUTE_UNUSED, |
| struct bfd_link_info *info) |
| { |
| bfd *dynobj; |
| asection *splt; |
| |
| if (!elf_hash_table (info)->dynamic_sections_created) |
| return true; |
| |
| /* As an extra sanity check, verify that all plt entries have been |
| filled in. However, relaxing might have changed the relocs so |
| that some plt entries don't get filled in, so we have to skip |
| this check if we're relaxing. Unfortunately, check_relocs is |
| called before relaxation. */ |
| |
| if (info->relax_trip > 0) |
| return true; |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| splt = elf_hash_table (info)->splt; |
| if (dynobj != NULL && splt != NULL) |
| { |
| bfd_byte *contents = splt->contents; |
| unsigned int i, size = splt->size; |
| |
| for (i = 0; i < size; i += 4) |
| { |
| unsigned int x = bfd_get_32 (dynobj, contents + i); |
| BFD_ASSERT (x != 0); |
| } |
| } |
| |
| return true; |
| } |
| |
| static bool |
| rl78_elf_always_size_sections (bfd *output_bfd ATTRIBUTE_UNUSED, |
| struct bfd_link_info *info) |
| { |
| bfd *dynobj; |
| asection *splt; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| dynobj = elf_hash_table (info)->dynobj; |
| if (dynobj == NULL) |
| return true; |
| |
| splt = elf_hash_table (info)->splt; |
| BFD_ASSERT (splt != NULL); |
| |
| splt->contents = (bfd_byte *) bfd_zalloc (dynobj, splt->size); |
| if (splt->contents == NULL) |
| return false; |
| |
| return true; |
| } |
| |
| |
| |
| /* Handle relaxing. */ |
| |
| /* A subroutine of rl78_elf_relax_section. If the global symbol H |
| is within the low 64k, remove any entry for it in the plt. */ |
| |
| struct relax_plt_data |
| { |
| asection *splt; |
| bool *again; |
| }; |
| |
| static bool |
| rl78_relax_plt_check (struct elf_link_hash_entry *h, void * xdata) |
| { |
| struct relax_plt_data *data = (struct relax_plt_data *) xdata; |
| |
| if (h->plt.offset != (bfd_vma) -1) |
| { |
| bfd_vma address; |
| |
| if (h->root.type == bfd_link_hash_undefined |
| || h->root.type == bfd_link_hash_undefweak) |
| address = 0; |
| else |
| address = (h->root.u.def.section->output_section->vma |
| + h->root.u.def.section->output_offset |
| + h->root.u.def.value); |
| |
| if (valid_16bit_address (address)) |
| { |
| h->plt.offset = -1; |
| data->splt->size -= 4; |
| *data->again = true; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* A subroutine of rl78_elf_relax_section. If the global symbol H |
| previously had a plt entry, give it a new entry offset. */ |
| |
| static bool |
| rl78_relax_plt_realloc (struct elf_link_hash_entry *h, void * xdata) |
| { |
| bfd_vma *entry = (bfd_vma *) xdata; |
| |
| if (h->plt.offset != (bfd_vma) -1) |
| { |
| h->plt.offset = *entry; |
| *entry += 4; |
| } |
| |
| return true; |
| } |
| |
| static bool |
| rl78_elf_relax_plt_section (bfd *dynobj, |
| asection *splt, |
| struct bfd_link_info *info, |
| bool *again) |
| { |
| struct relax_plt_data relax_plt_data; |
| bfd *ibfd; |
| |
| /* Assume nothing changes. */ |
| *again = false; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| /* We only relax the .plt section at the moment. */ |
| if (dynobj != elf_hash_table (info)->dynobj |
| || strcmp (splt->name, ".plt") != 0) |
| return true; |
| |
| /* Quick check for an empty plt. */ |
| if (splt->size == 0) |
| return true; |
| |
| /* Map across all global symbols; see which ones happen to |
| fall in the low 64k. */ |
| relax_plt_data.splt = splt; |
| relax_plt_data.again = again; |
| elf_link_hash_traverse (elf_hash_table (info), rl78_relax_plt_check, |
| &relax_plt_data); |
| |
| /* Likewise for local symbols, though that's somewhat less convenient |
| as we have to walk the list of input bfds and swap in symbol data. */ |
| for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link.next) |
| { |
| bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd); |
| Elf_Internal_Shdr *symtab_hdr; |
| Elf_Internal_Sym *isymbuf = NULL; |
| unsigned int idx; |
| |
| if (! local_plt_offsets) |
| continue; |
| |
| symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; |
| if (symtab_hdr->sh_info != 0) |
| { |
| isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; |
| if (isymbuf == NULL) |
| isymbuf = bfd_elf_get_elf_syms (ibfd, symtab_hdr, |
| symtab_hdr->sh_info, 0, |
| NULL, NULL, NULL); |
| if (isymbuf == NULL) |
| return false; |
| } |
| |
| for (idx = 0; idx < symtab_hdr->sh_info; ++idx) |
| { |
| Elf_Internal_Sym *isym; |
| asection *tsec; |
| bfd_vma address; |
| |
| if (local_plt_offsets[idx] == (bfd_vma) -1) |
| continue; |
| |
| isym = &isymbuf[idx]; |
| if (isym->st_shndx == SHN_UNDEF) |
| continue; |
| else if (isym->st_shndx == SHN_ABS) |
| tsec = bfd_abs_section_ptr; |
| else if (isym->st_shndx == SHN_COMMON) |
| tsec = bfd_com_section_ptr; |
| else |
| tsec = bfd_section_from_elf_index (ibfd, isym->st_shndx); |
| |
| address = (tsec->output_section->vma |
| + tsec->output_offset |
| + isym->st_value); |
| if (valid_16bit_address (address)) |
| { |
| local_plt_offsets[idx] = -1; |
| splt->size -= 4; |
| *again = true; |
| } |
| } |
| |
| if (isymbuf != NULL |
| && symtab_hdr->contents != (unsigned char *) isymbuf) |
| { |
| if (! info->keep_memory) |
| free (isymbuf); |
| else |
| { |
| /* Cache the symbols for elf_link_input_bfd. */ |
| symtab_hdr->contents = (unsigned char *) isymbuf; |
| } |
| } |
| } |
| |
| /* If we changed anything, walk the symbols again to reallocate |
| .plt entry addresses. */ |
| if (*again && splt->size > 0) |
| { |
| bfd_vma entry = 0; |
| |
| elf_link_hash_traverse (elf_hash_table (info), |
| rl78_relax_plt_realloc, &entry); |
| |
| for (ibfd = info->input_bfds; ibfd ; ibfd = ibfd->link.next) |
| { |
| bfd_vma *local_plt_offsets = elf_local_got_offsets (ibfd); |
| unsigned int nlocals = elf_tdata (ibfd)->symtab_hdr.sh_info; |
| unsigned int idx; |
| |
| if (! local_plt_offsets) |
| continue; |
| |
| for (idx = 0; idx < nlocals; ++idx) |
| if (local_plt_offsets[idx] != (bfd_vma) -1) |
| { |
| local_plt_offsets[idx] = entry; |
| entry += 4; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Delete some bytes from a section while relaxing. */ |
| |
| static bool |
| elf32_rl78_relax_delete_bytes (bfd *abfd, asection *sec, bfd_vma addr, int count, |
| Elf_Internal_Rela *alignment_rel, int force_snip) |
| { |
| Elf_Internal_Shdr * symtab_hdr; |
| unsigned int sec_shndx; |
| bfd_byte * contents; |
| Elf_Internal_Rela * irel; |
| Elf_Internal_Rela * irelend; |
| Elf_Internal_Sym * isym; |
| Elf_Internal_Sym * isymend; |
| bfd_vma toaddr; |
| unsigned int symcount; |
| struct elf_link_hash_entry ** sym_hashes; |
| struct elf_link_hash_entry ** end_hashes; |
| |
| if (!alignment_rel) |
| force_snip = 1; |
| |
| sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); |
| |
| contents = elf_section_data (sec)->this_hdr.contents; |
| |
| /* The deletion must stop at the next alignment boundary, if |
| ALIGNMENT_REL is non-NULL. */ |
| toaddr = sec->size; |
| if (alignment_rel) |
| toaddr = alignment_rel->r_offset; |
| |
| irel = elf_section_data (sec)->relocs; |
| if (irel == NULL) |
| { |
| _bfd_elf_link_read_relocs (sec->owner, sec, NULL, NULL, true); |
| irel = elf_section_data (sec)->relocs; |
| } |
| |
| irelend = irel + sec->reloc_count; |
| |
| /* Actually delete the bytes. */ |
| memmove (contents + addr, contents + addr + count, |
| (size_t) (toaddr - addr - count)); |
| |
| /* If we don't have an alignment marker to worry about, we can just |
| shrink the section. Otherwise, we have to fill in the newly |
| created gap with NOP insns (0x03). */ |
| if (force_snip) |
| sec->size -= count; |
| else |
| memset (contents + toaddr - count, 0x03, count); |
| |
| /* Adjust all the relocs. */ |
| for (; irel && irel < irelend; irel++) |
| { |
| /* Get the new reloc address. */ |
| if (irel->r_offset > addr |
| && (irel->r_offset < toaddr |
| || (force_snip && irel->r_offset == toaddr))) |
| irel->r_offset -= count; |
| |
| /* If we see an ALIGN marker at the end of the gap, we move it |
| to the beginning of the gap, since marking these gaps is what |
| they're for. */ |
| if (irel->r_offset == toaddr |
| && ELF32_R_TYPE (irel->r_info) == R_RL78_RH_RELAX |
| && irel->r_addend & RL78_RELAXA_ALIGN) |
| irel->r_offset -= count; |
| } |
| |
| /* Adjust the local symbols defined in this section. */ |
| symtab_hdr = &elf_tdata (abfd)->symtab_hdr; |
| isym = (Elf_Internal_Sym *) symtab_hdr->contents; |
| isymend = isym + symtab_hdr->sh_info; |
| |
| for (; isym < isymend; isym++) |
| { |
| /* If the symbol is in the range of memory we just moved, we |
| have to adjust its value. */ |
| if (isym->st_shndx == sec_shndx |
| && isym->st_value > addr |
| && isym->st_value < toaddr) |
| isym->st_value -= count; |
| |
| /* If the symbol *spans* the bytes we just deleted (i.e. it's |
| *end* is in the moved bytes but it's *start* isn't), then we |
| must adjust its size. */ |
| if (isym->st_shndx == sec_shndx |
| && isym->st_value < addr |
| && isym->st_value + isym->st_size > addr |
| && isym->st_value + isym->st_size < toaddr) |
| isym->st_size -= count; |
| } |
| |
| /* Now adjust the global symbols defined in this section. */ |
| symcount = (symtab_hdr->sh_size / sizeof (Elf32_External_Sym) |
| - symtab_hdr->sh_info); |
| sym_hashes = elf_sym_hashes (abfd); |
| end_hashes = sym_hashes + symcount; |
| |
| for (; sym_hashes < end_hashes; sym_hashes++) |
| { |
| struct elf_link_hash_entry *sym_hash = *sym_hashes; |
| |
| if ((sym_hash->root.type == bfd_link_hash_defined |
| || sym_hash->root.type == bfd_link_hash_defweak) |
| && sym_hash->root.u.def.section == sec) |
| { |
| /* As above, adjust the value if needed. */ |
| if (sym_hash->root.u.def.value > addr |
| && sym_hash->root.u.def.value < toaddr) |
| sym_hash->root.u.def.value -= count; |
| |
| /* As above, adjust the size if needed. */ |
| if (sym_hash->root.u.def.value < addr |
| && sym_hash->root.u.def.value + sym_hash->size > addr |
| && sym_hash->root.u.def.value + sym_hash->size < toaddr) |
| sym_hash->size -= count; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* Used to sort relocs by address. If relocs have the same address, |
| we maintain their relative order, except that R_RL78_RH_RELAX |
| alignment relocs must be the first reloc for any given address. */ |
| |
| static void |
| reloc_bubblesort (Elf_Internal_Rela * r, int count) |
| { |
| int i; |
| bool again; |
| bool swappit; |
| |
| /* This is almost a classic bubblesort. It's the slowest sort, but |
| we're taking advantage of the fact that the relocations are |
| mostly in order already (the assembler emits them that way) and |
| we need relocs with the same address to remain in the same |
| relative order. */ |
| again = true; |
| while (again) |
| { |
| again = false; |
| for (i = 0; i < count - 1; i ++) |
| { |
| if (r[i].r_offset > r[i + 1].r_offset) |
| swappit = true; |
| else if (r[i].r_offset < r[i + 1].r_offset) |
| swappit = false; |
| else if (ELF32_R_TYPE (r[i + 1].r_info) == R_RL78_RH_RELAX |
| && (r[i + 1].r_addend & RL78_RELAXA_ALIGN)) |
| swappit = true; |
| else if (ELF32_R_TYPE (r[i + 1].r_info) == R_RL78_RH_RELAX |
| && (r[i + 1].r_addend & RL78_RELAXA_ELIGN) |
| && !(ELF32_R_TYPE (r[i].r_info) == R_RL78_RH_RELAX |
| && (r[i].r_addend & RL78_RELAXA_ALIGN))) |
| swappit = true; |
| else |
| swappit = false; |
| |
| if (swappit) |
| { |
| Elf_Internal_Rela tmp; |
| |
| tmp = r[i]; |
| r[i] = r[i + 1]; |
| r[i + 1] = tmp; |
| /* If we do move a reloc back, re-scan to see if it |
| needs to be moved even further back. This avoids |
| most of the O(n^2) behavior for our cases. */ |
| if (i > 0) |
| i -= 2; |
| again = true; |
| } |
| } |
| } |
| } |
| |
| |
| #define OFFSET_FOR_RELOC(rel, lrel, scale) \ |
| rl78_offset_for_reloc (abfd, rel + 1, symtab_hdr, shndx_buf, intsyms, \ |
| lrel, abfd, sec, link_info, scale) |
| |
| static bfd_vma |
| rl78_offset_for_reloc (bfd * abfd, |
| Elf_Internal_Rela * rel, |
| Elf_Internal_Shdr * symtab_hdr, |
| bfd_byte * shndx_buf ATTRIBUTE_UNUSED, |
| Elf_Internal_Sym * intsyms, |
| Elf_Internal_Rela ** lrel, |
| bfd * input_bfd, |
| asection * input_section, |
| struct bfd_link_info * info, |
| int * scale) |
| { |
| bfd_vma symval; |
| |
| *scale = 1; |
| |
| /* REL is the first of 1..N relocations. We compute the symbol |
| value for each relocation, then combine them if needed. LREL |
| gets a pointer to the last relocation used. */ |
| while (1) |
| { |
| unsigned long r_type; |
| |
| /* Get the value of the symbol referred to by the reloc. */ |
| if (ELF32_R_SYM (rel->r_info) < symtab_hdr->sh_info) |
| { |
| /* A local symbol. */ |
| Elf_Internal_Sym *isym; |
| asection *ssec; |
| |
| isym = intsyms + ELF32_R_SYM (rel->r_info); |
| |
| if (isym->st_shndx == SHN_UNDEF) |
| ssec = bfd_und_section_ptr; |
| else if (isym->st_shndx == SHN_ABS) |
| ssec = bfd_abs_section_ptr; |
| else if (isym->st_shndx == SHN_COMMON) |
| ssec = bfd_com_section_ptr; |
| else |
| ssec = bfd_section_from_elf_index (abfd, |
| isym->st_shndx); |
| |
| /* Initial symbol value. */ |
| symval = isym->st_value; |
| |
| /* GAS may have made this symbol relative to a section, in |
| which case, we have to add the addend to find the |
| symbol. */ |
| if (ELF_ST_TYPE (isym->st_info) == STT_SECTION) |
| symval += rel->r_addend; |
| |
| if (ssec) |
| { |
| if ((ssec->flags & SEC_MERGE) |
| && ssec->sec_info_type == SEC_INFO_TYPE_MERGE) |
| symval = _bfd_merged_section_offset (abfd, & ssec, |
| elf_section_data (ssec)->sec_info, |
| symval); |
| } |
| |
| /* Now make the offset relative to where the linker is putting it. */ |
| if (ssec) |
| symval += |
| ssec->output_section->vma + ssec->output_offset; |
| |
| symval += rel->r_addend; |
| } |
| else |
| { |
| unsigned long indx; |
| struct elf_link_hash_entry * h; |
| |
| /* An external symbol. */ |
| indx = ELF32_R_SYM (rel->r_info) - symtab_hdr->sh_info; |
| h = elf_sym_hashes (abfd)[indx]; |
| BFD_ASSERT (h != NULL); |
| |
| if (h->root.type != bfd_link_hash_defined |
| && h->root.type != bfd_link_hash_defweak) |
| { |
| /* This appears to be a reference to an undefined |
| symbol. Just ignore it--it will be caught by the |
| regular reloc processing. */ |
| if (lrel) |
| *lrel = rel; |
| return 0; |
| } |
| |
| symval = (h->root.u.def.value |
| + h->root.u.def.section->output_section->vma |
| + h->root.u.def.section->output_offset); |
| |
| symval += rel->r_addend; |
| } |
| |
| r_type = ELF32_R_TYPE (rel->r_info); |
| switch (r_type) |
| { |
| case R_RL78_SYM: |
| (void) rl78_compute_complex_reloc (r_type, symval, input_section, |
| NULL, NULL); |
| break; |
| |
| case R_RL78_OPromtop: |
| symval = get_romstart (info, input_bfd, input_section, rel->r_offset); |
| (void) rl78_compute_complex_reloc (r_type, symval, input_section, |
| NULL, NULL); |
| break; |
| |
| case R_RL78_OPramtop: |
| symval = get_ramstart (info, input_bfd, input_section, rel->r_offset); |
| (void) rl78_compute_complex_reloc (r_type, symval, input_section, |
| NULL, NULL); |
| break; |
| |
| case R_RL78_OPneg: |
| case R_RL78_OPadd: |
| case R_RL78_OPsub: |
| case R_RL78_OPmul: |
| case R_RL78_OPdiv: |
| case R_RL78_OPshla: |
| case R_RL78_OPshra: |
| case R_RL78_OPsctsize: |
| case R_RL78_OPscttop: |
| case R_RL78_OPand: |
| case R_RL78_OPor: |
| case R_RL78_OPxor: |
| case R_RL78_OPnot: |
| case R_RL78_OPmod: |
| (void) rl78_compute_complex_reloc (r_type, 0, input_section, |
| NULL, NULL); |
| break; |
| |
| case R_RL78_DIR16UL: |
| case R_RL78_DIR8UL: |
| case R_RL78_ABS16UL: |
| case R_RL78_ABS8UL: |
| *scale = 4; |
| goto reloc_computes_value; |
| |
| case R_RL78_DIR16UW: |
| case R_RL78_DIR8UW: |
| case R_RL78_ABS16UW: |
| case R_RL78_ABS8UW: |
| *scale = 2; |
| goto reloc_computes_value; |
| |
| default: |
| reloc_computes_value: |
| symval = rl78_compute_complex_reloc (r_type, symval, input_section, |
| NULL, NULL); |
| /* Fall through. */ |
| case R_RL78_DIR32: |
| case R_RL78_DIR24S: |
| case R_RL78_DIR16: |
| case R_RL78_DIR16U: |
| case R_RL78_DIR16S: |
| case R_RL78_DIR24S_PCREL: |
| case R_RL78_DIR16S_PCREL: |
| case R_RL78_DIR8S_PCREL: |
| if (lrel) |
| *lrel = rel; |
| return symval; |
| } |
| |
| rel ++; |
| } |
| } |
| |
| const struct { |
| int prefix; /* or -1 for "no prefix" */ |
| int insn; /* or -1 for "end of list" */ |
| int insn_for_saddr; /* or -1 for "no alternative" */ |
| int insn_for_sfr; /* or -1 for "no alternative" */ |
| } relax_addr16[] = { |
| { -1, 0x02, 0x06, -1 }, /* ADDW AX, !addr16 */ |
| { -1, 0x22, 0x26, -1 }, /* SUBW AX, !addr16 */ |
| { -1, 0x42, 0x46, -1 }, /* CMPW AX, !addr16 */ |
| { -1, 0x40, 0x4a, -1 }, /* CMP !addr16, #byte */ |
| |
| { -1, 0x0f, 0x0b, -1 }, /* ADD A, !addr16 */ |
| { -1, 0x1f, 0x1b, -1 }, /* ADDC A, !addr16 */ |
| { -1, 0x2f, 0x2b, -1 }, /* SUB A, !addr16 */ |
| { -1, 0x3f, 0x3b, -1 }, /* SUBC A, !addr16 */ |
| { -1, 0x4f, 0x4b, -1 }, /* CMP A, !addr16 */ |
| { -1, 0x5f, 0x5b, -1 }, /* AND A, !addr16 */ |
| { -1, 0x6f, 0x6b, -1 }, /* OR A, !addr16 */ |
| { -1, 0x7f, 0x7b, -1 }, /* XOR A, !addr16 */ |
| |
| { -1, 0x8f, 0x8d, 0x8e }, /* MOV A, !addr16 */ |
| { -1, 0x9f, 0x9d, 0x9e }, /* MOV !addr16, A */ |
| { -1, 0xaf, 0xad, 0xae }, /* MOVW AX, !addr16 */ |
| { -1, 0xbf, 0xbd, 0xbe }, /* MOVW !addr16, AX */ |
| { -1, 0xcf, 0xcd, 0xce }, /* MOVW !addr16, #word */ |
| |
| { -1, 0xa0, 0xa4, -1 }, /* INC !addr16 */ |
| { -1, 0xa2, 0xa6, -1 }, /* INCW !addr16 */ |
| { -1, 0xb0, 0xb4, -1 }, /* DEC !addr16 */ |
| { -1, 0xb2, 0xb6, -1 }, /* DECW !addr16 */ |
| |
| { -1, 0xd5, 0xd4, -1 }, /* CMP0 !addr16 */ |
| { -1, 0xe5, 0xe4, -1 }, /* ONEB !addr16 */ |
| { -1, 0xf5, 0xf4, -1 }, /* CLRB !addr16 */ |
| |
| { -1, 0xd9, 0xd8, -1 }, /* MOV X, !addr16 */ |
| { -1, 0xe9, 0xe8, -1 }, /* MOV B, !addr16 */ |
| { -1, 0xf9, 0xf8, -1 }, /* MOV C, !addr16 */ |
| { -1, 0xdb, 0xda, -1 }, /* MOVW BC, !addr16 */ |
| { -1, 0xeb, 0xea, -1 }, /* MOVW DE, !addr16 */ |
| { -1, 0xfb, 0xfa, -1 }, /* MOVW HL, !addr16 */ |
| |
| { 0x61, 0xaa, 0xa8, -1 }, /* XCH A, !addr16 */ |
| |
| { 0x71, 0x00, 0x02, 0x0a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x10, 0x12, 0x1a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x20, 0x22, 0x2a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x30, 0x32, 0x3a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x40, 0x42, 0x4a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x50, 0x52, 0x5a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x60, 0x62, 0x6a }, /* SET1 !addr16.0 */ |
| { 0x71, 0x70, 0x72, 0x7a }, /* SET1 !addr16.0 */ |
| |
| { 0x71, 0x08, 0x03, 0x0b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x18, 0x13, 0x1b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x28, 0x23, 0x2b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x38, 0x33, 0x3b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x48, 0x43, 0x4b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x58, 0x53, 0x5b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x68, 0x63, 0x6b }, /* CLR1 !addr16.0 */ |
| { 0x71, 0x78, 0x73, 0x7b }, /* CLR1 !addr16.0 */ |
| |
| { -1, -1, -1, -1 } |
| }; |
| |
| /* Relax one section. */ |
| |
| static bool |
| rl78_elf_relax_section (bfd *abfd, |
| asection *sec, |
| struct bfd_link_info *link_info, |
| bool *again) |
| { |
| Elf_Internal_Shdr * symtab_hdr; |
| Elf_Internal_Shdr * shndx_hdr; |
| Elf_Internal_Rela * internal_relocs; |
| Elf_Internal_Rela * free_relocs = NULL; |
| Elf_Internal_Rela * irel; |
| Elf_Internal_Rela * srel; |
| Elf_Internal_Rela * irelend; |
| Elf_Internal_Rela * next_alignment; |
| bfd_byte * contents = NULL; |
| bfd_byte * free_contents = NULL; |
| Elf_Internal_Sym * intsyms = NULL; |
| Elf_Internal_Sym * free_intsyms = NULL; |
| bfd_byte * shndx_buf = NULL; |
| bfd_vma pc; |
| bfd_vma symval ATTRIBUTE_UNUSED = 0; |
| int pcrel ATTRIBUTE_UNUSED = 0; |
| int code ATTRIBUTE_UNUSED = 0; |
| int section_alignment_glue; |
| int scale; |
| |
| if (abfd == elf_hash_table (link_info)->dynobj |
| && strcmp (sec->name, ".plt") == 0) |
| return rl78_elf_relax_plt_section (abfd, sec, link_info, again); |
| |
| /* Assume nothing changes. */ |
| *again = false; |
| |
| /* We don't have to do anything for a relocatable link, if |
| this section does not have relocs, or if this is not a |
| code section. */ |
| if (bfd_link_relocatable (link_info) |
| || (sec->flags & SEC_RELOC) == 0 |
| || sec->reloc_count == 0 |
| || (sec->flags & SEC_CODE) == 0) |
| return true; |
| |
| symtab_hdr = & elf_symtab_hdr (abfd); |
| if (elf_symtab_shndx_list (abfd)) |
| shndx_hdr = & elf_symtab_shndx_list (abfd)->hdr; |
| else |
| shndx_hdr = NULL; |
| |
| /* Get the section contents. */ |
| if (elf_section_data (sec)->this_hdr.contents != NULL) |
| contents = elf_section_data (sec)->this_hdr.contents; |
| /* Go get them off disk. */ |
| else |
| { |
| if (! bfd_malloc_and_get_section (abfd, sec, &contents)) |
| goto error_return; |
| elf_section_data (sec)->this_hdr.contents = contents; |
| } |
| |
| /* Read this BFD's symbols. */ |
| /* Get cached copy if it exists. */ |
| if (symtab_hdr->contents != NULL) |
| intsyms = (Elf_Internal_Sym *) symtab_hdr->contents; |
| else |
| { |
| intsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr, symtab_hdr->sh_info, 0, NULL, NULL, NULL); |
| symtab_hdr->contents = (bfd_byte *) intsyms; |
| } |
| |
| if (shndx_hdr && shndx_hdr->sh_size != 0) |
| { |
| size_t amt; |
| |
| if (_bfd_mul_overflow (symtab_hdr->sh_info, |
| sizeof (Elf_External_Sym_Shndx), &amt)) |
| { |
| bfd_set_error (bfd_error_no_memory); |
| goto error_return; |
| } |
| if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0) |
| goto error_return; |
| shndx_buf = _bfd_malloc_and_read (abfd, amt, amt); |
| if (shndx_buf == NULL) |
| goto error_return; |
| shndx_hdr->contents = shndx_buf; |
| } |
| |
| /* Get a copy of the native relocations. */ |
| internal_relocs = (_bfd_elf_link_read_relocs |
| (abfd, sec, NULL, (Elf_Internal_Rela *) NULL, |
| link_info->keep_memory)); |
| if (internal_relocs == NULL) |
| goto error_return; |
| if (! link_info->keep_memory) |
| free_relocs = internal_relocs; |
| |
| /* The RL_ relocs must be just before the operand relocs they go |
| with, so we must sort them to guarantee this. We use bubblesort |
| instead of qsort so we can guarantee that relocs with the same |
| address remain in the same relative order. */ |
| reloc_bubblesort (internal_relocs, sec->reloc_count); |
| |
| /* Walk through them looking for relaxing opportunities. */ |
| irelend = internal_relocs + sec->reloc_count; |
| |
| |
| /* This will either be NULL or a pointer to the next alignment |
| relocation. */ |
| next_alignment = internal_relocs; |
| |
| /* We calculate worst case shrinkage caused by alignment directives. |
| No fool-proof, but better than either ignoring the problem or |
| doing heavy duty analysis of all the alignment markers in all |
| input sections. */ |
| section_alignment_glue = 0; |
| for (irel = internal_relocs; irel < irelend; irel++) |
| if (ELF32_R_TYPE (irel->r_info) == R_RL78_RH_RELAX |
| && irel->r_addend & RL78_RELAXA_ALIGN) |
| { |
| int this_glue = 1 << (irel->r_addend & RL78_RELAXA_ANUM); |
| |
| if (section_alignment_glue < this_glue) |
| section_alignment_glue = this_glue; |
| } |
| /* Worst case is all 0..N alignments, in order, causing 2*N-1 byte |
| shrinkage. */ |
| section_alignment_glue *= 2; |
| |
| for (irel = internal_relocs; irel < irelend; irel++) |
| { |
| unsigned char *insn; |
| int nrelocs; |
| |
| /* The insns we care about are all marked with one of these. */ |
| if (ELF32_R_TYPE (irel->r_info) != R_RL78_RH_RELAX) |
| continue; |
| |
| if (irel->r_addend & RL78_RELAXA_ALIGN |
| || next_alignment == internal_relocs) |
| { |
| /* When we delete bytes, we need to maintain all the alignments |
| indicated. In addition, we need to be careful about relaxing |
| jumps across alignment boundaries - these displacements |
| *grow* when we delete bytes. For now, don't shrink |
| displacements across an alignment boundary, just in case. |
| Note that this only affects relocations to the same |
| section. */ |
| next_alignment += 2; |
| while (next_alignment < irelend |
| && (ELF32_R_TYPE (next_alignment->r_info) != R_RL78_RH_RELAX |
| || !(next_alignment->r_addend & RL78_RELAXA_ELIGN))) |
| next_alignment ++; |
| if (next_alignment >= irelend || next_alignment->r_offset == 0) |
| next_alignment = NULL; |
| } |
| |
| /* When we hit alignment markers, see if we've shrunk enough |
| before them to reduce the gap without violating the alignment |
| requirements. */ |
| if (irel->r_addend & RL78_RELAXA_ALIGN) |
| { |
| /* At this point, the next relocation *should* be the ELIGN |
| end marker. */ |
| Elf_Internal_Rela *erel = irel + 1; |
| unsigned int alignment, nbytes; |
| |
| if (ELF32_R_TYPE (erel->r_info) != R_RL78_RH_RELAX) |
| continue; |
| if (!(erel->r_addend & RL78_RELAXA_ELIGN)) |
| continue; |
| |
| alignment = 1 << (irel->r_addend & RL78_RELAXA_ANUM); |
| |
| if (erel->r_offset - irel->r_offset < alignment) |
| continue; |
| |
| nbytes = erel->r_offset - irel->r_offset; |
| nbytes /= alignment; |
| nbytes *= alignment; |
| |
| elf32_rl78_relax_delete_bytes (abfd, sec, erel->r_offset - nbytes, nbytes, |
| next_alignment, erel->r_offset == sec->size); |
| *again = true; |
| |
| continue; |
| } |
| |
| if (irel->r_addend & RL78_RELAXA_ELIGN) |
| continue; |
| |
| insn = contents + irel->r_offset; |
| |
| nrelocs = irel->r_addend & RL78_RELAXA_RNUM; |
| |
| /* At this point, we have an insn that is a candidate for linker |
| relaxation. There are NRELOCS relocs following that may be |
| relaxed, although each reloc may be made of more than one |
| reloc entry (such as gp-rel symbols). */ |
| |
| /* Get the value of the symbol referred to by the reloc. Just |
| in case this is the last reloc in the list, use the RL's |
| addend to choose between this reloc (no addend) or the next |
| (yes addend, which means at least one following reloc). */ |
| |
| /* srel points to the "current" reloction for this insn - |
| actually the last reloc for a given operand, which is the one |
| we need to update. We check the relaxations in the same |
| order that the relocations happen, so we'll just push it |
| along as we go. */ |
| srel = irel; |
| |
| pc = sec->output_section->vma + sec->output_offset |
| + srel->r_offset; |
| |
| #define GET_RELOC \ |
| BFD_ASSERT (nrelocs > 0); \ |
| symval = OFFSET_FOR_RELOC (srel, &srel, &scale); \ |
| pcrel = symval - pc + srel->r_addend; \ |
| nrelocs --; |
| |
| #define SNIPNR(offset, nbytes) \ |
| elf32_rl78_relax_delete_bytes (abfd, sec, (insn - contents) + offset, nbytes, next_alignment, 0); |
| |
| #define SNIP(offset, nbytes, newtype) \ |
| SNIPNR (offset, nbytes); \ |
| srel->r_info = ELF32_R_INFO (ELF32_R_SYM (srel->r_info), newtype) |
| |
| /* The order of these bit tests must match the order that the |
| relocs appear in. Since we sorted those by offset, we can |
| predict them. */ |
| |
| /*----------------------------------------------------------------------*/ |
| /* EF ad BR $rel8 pcrel |
| ED al ah BR !abs16 abs |
| EE al ah BR $!rel16 pcrel |
| EC al ah as BR !!abs20 abs |
| |
| FD al ah CALL !abs16 abs |
| FE al ah CALL $!rel16 pcrel |
| FC al ah as CALL !!abs20 abs |
| |
| DC ad BC $rel8 |
| DE ad BNC $rel8 |
| DD ad BZ $rel8 |
| DF ad BNZ $rel8 |
| 61 C3 ad BH $rel8 |
| 61 D3 ad BNH $rel8 |
| 61 C8 EF ad SKC ; BR $rel8 |
| 61 D8 EF ad SKNC ; BR $rel8 |
| 61 E8 EF ad SKZ ; BR $rel8 |
| 61 F8 EF ad SKNZ ; BR $rel8 |
| 61 E3 EF ad SKH ; BR $rel8 |
| 61 F3 EF ad SKNH ; BR $rel8 |
| */ |
| |
| if ((irel->r_addend & RL78_RELAXA_MASK) == RL78_RELAXA_BRA) |
| { |
| /* SKIP opcodes that skip non-branches will have a relax tag |
| but no corresponding symbol to relax against; we just |
| skip those. */ |
| if (irel->r_addend & RL78_RELAXA_RNUM) |
| { |
| GET_RELOC; |
| } |
| |
| switch (insn[0]) |
| { |
| case 0xdc: /* BC */ |
| case 0xdd: /* BZ */ |
| case 0xde: /* BNC */ |
| case 0xdf: /* BNZ */ |
| if (insn[1] == 0x03 && insn[2] == 0xee /* BR */ |
| && (srel->r_offset - irel->r_offset) > 1) /* a B<c> without its own reloc */ |
| { |
| /* This is a "long" conditional as generated by gas: |
| DC 03 EE ad.dr */ |
| if (pcrel < 127 |
| && pcrel > -127) |
| { |
| insn[0] ^= 0x02; /* invert conditional */ |
| SNIPNR (4, 1); |
| SNIP (1, 2, R_RL78_DIR8S_PCREL); |
| insn[1] = pcrel; |
| *again = true; |
| } |
| } |
| break; |
| |
| case 0xec: /* BR !!abs20 */ |
| |
| if (pcrel < 127 |
| && pcrel > -127) |
| { |
| insn[0] = 0xef; |
| insn[1] = pcrel; |
| SNIP (2, 2, R_RL78_DIR8S_PCREL); |
| *again = true; |
| } |
| else if (symval < 65536) |
| { |
| insn[0] = 0xed; |
| insn[1] = symval & 0xff; |
| insn[2] = symval >> 8; |
| SNIP (2, 1, R_RL78_DIR16U); |
| *again = true; |
| } |
| else if (pcrel < 32767 |
| && pcrel > -32767) |
| { |
| insn[0] = 0xee; |
| insn[1] = pcrel & 0xff; |
| insn[2] = pcrel >> 8; |
| SNIP (2, 1, R_RL78_DIR16S_PCREL); |
| *again = true; |
| } |
| break; |
| |
| case 0xee: /* BR $!pcrel16 */ |
| case 0xed: /* BR $!abs16 */ |
| if (pcrel < 127 |
| && pcrel > -127) |
| { |
| insn[0] = 0xef; |
| insn[1] = pcrel; |
| SNIP (2, 1, R_RL78_DIR8S_PCREL); |
| *again = true; |
| } |
| break; |
| |
| case 0xfc: /* CALL !!abs20 */ |
| if (symval < 65536) |
| { |
| insn[0] = 0xfd; |
| insn[1] = symval & 0xff; |
| insn[2] = symval >> 8; |
| SNIP (2, 1, R_RL78_DIR16U); |
| *again = true; |
| } |
| else if (pcrel < 32767 |
| && pcrel > -32767) |
| { |
| insn[0] = 0xfe; |
| insn[1] = pcrel & 0xff; |
| insn[2] = pcrel >> 8; |
| SNIP (2, 1, R_RL78_DIR16S_PCREL); |
| *again = true; |
| } |
| break; |
| |
| case 0x61: /* PREFIX */ |
| /* For SKIP/BR, we change the BR opcode and delete the |
| SKIP. That way, we don't have to find and change the |
| relocation for the BR. */ |
| /* Note that, for the case where we're skipping some |
| other insn, we have no "other" reloc but that's safe |
| here anyway. */ |
| switch (insn[1]) |
| { |
| case 0xd3: /* BNH */ |
| case 0xc3: /* BH */ |
| if (insn[2] == 0x03 && insn[3] == 0xee |
| && (srel->r_offset - irel->r_offset) > 2) /* a B<c> without its own reloc */ |
| { |
| /* Another long branch by gas: |
| 61 D3 03 EE ad.dr */ |
| if (pcrel < 127 |
| && pcrel > -127) |
| { |
| insn[1] ^= 0x10; /* invert conditional */ |
| SNIPNR (5, 1); |
| SNIP (2, 2, R_RL78_DIR8S_PCREL); |
| insn[2] = pcrel; |
| *again = true; |
| } |
| } |
| break; |
| |
| case 0xc8: /* SKC */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xde; /* BNC */ |
| SNIPNR (0, 2); |
| } |
| break; |
| |
| case 0xd8: /* SKNC */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xdc; /* BC */ |
| SNIPNR (0, 2); |
| } |
| break; |
| |
| case 0xe8: /* SKZ */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xdf; /* BNZ */ |
| SNIPNR (0, 2); |
| } |
| break; |
| |
| case 0xf8: /* SKNZ */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xdd; /* BZ */ |
| SNIPNR (0, 2); |
| } |
| break; |
| |
| case 0xe3: /* SKH */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xd3; /* BNH */ |
| SNIPNR (1, 1); /* we reuse the 0x61 prefix from the SKH */ |
| } |
| break; |
| |
| case 0xf3: /* SKNH */ |
| if (insn[2] == 0xef) |
| { |
| insn[2] = 0xc3; /* BH */ |
| SNIPNR (1, 1); /* we reuse the 0x61 prefix from the SKH */ |
| } |
| break; |
| } |
| break; |
| } |
| } |
| |
| if ((irel->r_addend & RL78_RELAXA_MASK) == RL78_RELAXA_ADDR16 |
| && nrelocs > 0) |
| { |
| /*----------------------------------------------------------------------*/ |
| /* Some insns have both a 16-bit address operand and an 8-bit |
| variant if the address is within a special range: |
| |
| Address 16-bit operand SADDR range SFR range |
| FFF00-FFFFF 0xff00-0xffff 0x00-0xff |
| FFE20-FFF1F 0xfe20-0xff1f 0x00-0xff |
| |
| The RELAX_ADDR16[] array has the insn encodings for the |
| 16-bit operand version, as well as the SFR and SADDR |
| variants. We only need to replace the encodings and |
| adjust the operand. |
| |
| Note: we intentionally do not attempt to decode and skip |
| any ES: prefix, as adding ES: means the addr16 (likely) |
| no longer points to saddr/sfr space. |
| */ |
| |
| int is_sfr; |
| int is_saddr; |
| int idx; |
| int poff; |
| |
| GET_RELOC; |
| |
| if (0xffe20 <= symval && symval <= 0xfffff) |
| { |
| |
| is_saddr = (0xffe20 <= symval && symval <= 0xfff1f); |
| is_sfr = (0xfff00 <= symval && symval <= 0xfffff); |
| |
| for (idx = 0; relax_addr16[idx].insn != -1; idx ++) |
| { |
| if (relax_addr16[idx].prefix != -1 |
| && insn[0] == relax_addr16[idx].prefix |
| && insn[1] == relax_addr16[idx].insn) |
| { |
| poff = 1; |
| } |
| else if (relax_addr16[idx].prefix == -1 |
| && insn[0] == relax_addr16[idx].insn) |
| { |
| poff = 0; |
| } |
| else |
| continue; |
| |
| /* We have a matched insn, and poff is 0 or 1 depending |
| on the base pattern size. */ |
| |
| if (is_sfr && relax_addr16[idx].insn_for_sfr != -1) |
| { |
| insn[poff] = relax_addr16[idx].insn_for_sfr; |
| SNIP (poff+2, 1, R_RL78_RH_SFR); |
| } |
| |
| else if (is_saddr && relax_addr16[idx].insn_for_saddr != -1) |
| { |
| insn[poff] = relax_addr16[idx].insn_for_saddr; |
| SNIP (poff+2, 1, R_RL78_RH_SADDR); |
| } |
| } |
| } |
| } |
| /*----------------------------------------------------------------------*/ |
| } |
| |
| return true; |
| |
| error_return: |
| free (free_relocs); |
| free (free_contents); |
| |
| if (shndx_buf != NULL) |
| { |
| shndx_hdr->contents = NULL; |
| free (shndx_buf); |
| } |
| |
| free (free_intsyms); |
| |
| return true; |
| } |
| |
| |
| |
| #define ELF_ARCH bfd_arch_rl78 |
| #define ELF_MACHINE_CODE EM_RL78 |
| #define ELF_MAXPAGESIZE 0x1000 |
| |
| #define TARGET_LITTLE_SYM rl78_elf32_vec |
| #define TARGET_LITTLE_NAME "elf32-rl78" |
| |
| #define elf_info_to_howto_rel NULL |
| #define elf_info_to_howto rl78_info_to_howto_rela |
| #define elf_backend_object_p rl78_elf_object_p |
| #define elf_backend_relocate_section rl78_elf_relocate_section |
| #define elf_symbol_leading_char ('_') |
| #define elf_backend_can_gc_sections 1 |
| |
| #define bfd_elf32_bfd_reloc_type_lookup rl78_reloc_type_lookup |
| #define bfd_elf32_bfd_reloc_name_lookup rl78_reloc_name_lookup |
| #define bfd_elf32_bfd_set_private_flags rl78_elf_set_private_flags |
| #define bfd_elf32_bfd_merge_private_bfd_data rl78_elf_merge_private_bfd_data |
| #define bfd_elf32_bfd_print_private_bfd_data rl78_elf_print_private_bfd_data |
| |
| #define bfd_elf32_bfd_relax_section rl78_elf_relax_section |
| #define elf_backend_check_relocs rl78_elf_check_relocs |
| #define elf_backend_always_size_sections \ |
| rl78_elf_always_size_sections |
| #define elf_backend_finish_dynamic_sections \ |
| rl78_elf_finish_dynamic_sections |
| |
| #include "elf32-target.h" |