From 889294f6ffb380eb37b1f1f3bd22807fa9204c14 Mon Sep 17 00:00:00 2001 From: Dimitar Dimitrov Date: Fri, 30 Dec 2016 12:39:46 +0200 Subject: PRU BFD support include/ * elf/common.h: Add PRU ELF. * elf/pru.h: New file. * opcode/pru.h: New file. * dis-asm.h (print_insn_pru): Declare. bfd/ * archures.c: Add bfd_arch_pru. * Makefile.am: Add PRU target. * config.bfd: Ditto. * configure.ac: Ditto. * elf-bfd.h (enum elf_target_id): Add PRU_ELF_DATA. * targets.c: Add pru_elf32_vec. * reloc.c: Add PRU relocations. * cpu-pru.c: New file. * elf32-pru.c: New file. * Makefile.in: Regenerate. * configure: Regenerate. * po/SRC-POTFILES.in: Regenerate. * bfd-in2.h: Regenerate * libbfd.h: Regenerate. Signed-off-by: Dimitar Dimitrov --- bfd/ChangeLog | 17 + bfd/Makefile.am | 4 + bfd/Makefile.in | 6 + bfd/archures.c | 4 + bfd/bfd-in2.h | 37 ++ bfd/config.bfd | 6 + bfd/configure | 1 + bfd/configure.ac | 1 + bfd/cpu-pru.c | 43 ++ bfd/elf-bfd.h | 1 + bfd/elf32-pru.c | 1469 ++++++++++++++++++++++++++++++++++++++++++++++++ bfd/libbfd.h | 12 + bfd/po/SRC-POTFILES.in | 2 + bfd/reloc.c | 48 ++ bfd/targets.c | 3 + include/ChangeLog | 7 + include/dis-asm.h | 1 + include/elf/common.h | 2 +- include/elf/pru.h | 55 ++ include/opcode/pru.h | 411 ++++++++++++++ 20 files changed, 2129 insertions(+), 1 deletion(-) create mode 100644 bfd/cpu-pru.c create mode 100644 bfd/elf32-pru.c create mode 100644 include/elf/pru.h create mode 100644 include/opcode/pru.h diff --git a/bfd/ChangeLog b/bfd/ChangeLog index bee5e40..a9b2e09 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,20 @@ +2016-12-31 Dimitar Dimitrov + + * archures.c: Add bfd_arch_pru. + * Makefile.am: Add PRU target. + * config.bfd: Ditto. + * configure.ac: Ditto. + * elf-bfd.h (enum elf_target_id): Add PRU_ELF_DATA. + * targets.c: Add pru_elf32_vec. + * reloc.c: Add PRU relocations. + * cpu-pru.c: New file. + * elf32-pru.c: New file. + * Makefile.in: Regenerate. + * configure: Regenerate. + * po/SRC-POTFILES.in: Regenerate. + * bfd-in2.h: Regenerate + * libbfd.h: Regenerate. + 2016-12-29 Alan Modra * elflink.c (_bfd_elf_link_hash_copy_indirect): Only omit diff --git a/bfd/Makefile.am b/bfd/Makefile.am index 0d5dd4a..ff02ebb 100644 --- a/bfd/Makefile.am +++ b/bfd/Makefile.am @@ -147,6 +147,7 @@ ALL_MACHINES = \ cpu-pj.lo \ cpu-plugin.lo \ cpu-powerpc.lo \ + cpu-pru.lo \ cpu-rs6000.lo \ cpu-riscv.lo \ cpu-rl78.lo \ @@ -235,6 +236,7 @@ ALL_MACHINES_CFILES = \ cpu-pj.c \ cpu-plugin.c \ cpu-powerpc.c \ + cpu-pru.c \ cpu-rs6000.c \ cpu-riscv.c \ cpu-rl78.c \ @@ -365,6 +367,7 @@ BFD32_BACKENDS = \ elf32-or1k.lo \ elf32-pj.lo \ elf32-ppc.lo \ + elf32-pru.lo \ elf32-rl78.lo \ elf32-rx.lo \ elf32-s390.lo \ @@ -557,6 +560,7 @@ BFD32_BACKENDS_CFILES = \ elf32-or1k.c \ elf32-pj.c \ elf32-ppc.c \ + elf32-pru.c \ elf32-rl78.c \ elf32-rx.c \ elf32-s390.c \ diff --git a/bfd/Makefile.in b/bfd/Makefile.in index 661155c..7792dad 100644 --- a/bfd/Makefile.in +++ b/bfd/Makefile.in @@ -480,6 +480,7 @@ ALL_MACHINES = \ cpu-pj.lo \ cpu-plugin.lo \ cpu-powerpc.lo \ + cpu-pru.lo \ cpu-rs6000.lo \ cpu-riscv.lo \ cpu-rl78.lo \ @@ -568,6 +569,7 @@ ALL_MACHINES_CFILES = \ cpu-pj.c \ cpu-plugin.c \ cpu-powerpc.c \ + cpu-pru.c \ cpu-rs6000.c \ cpu-riscv.c \ cpu-rl78.c \ @@ -699,6 +701,7 @@ BFD32_BACKENDS = \ elf32-or1k.lo \ elf32-pj.lo \ elf32-ppc.lo \ + elf32-pru.lo \ elf32-rl78.lo \ elf32-rx.lo \ elf32-s390.lo \ @@ -891,6 +894,7 @@ BFD32_BACKENDS_CFILES = \ elf32-or1k.c \ elf32-pj.c \ elf32-ppc.c \ + elf32-pru.c \ elf32-rl78.c \ elf32-rx.c \ elf32-s390.c \ @@ -1420,6 +1424,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-pj.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-plugin.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-powerpc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-pru.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-riscv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-rl78.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-rs6000.Plo@am__quote@ @@ -1510,6 +1515,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-or1k.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-pj.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-ppc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-pru.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-riscv.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-rl78.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf32-rx.Plo@am__quote@ diff --git a/bfd/archures.c b/bfd/archures.c index e503492..1b1e9ce 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -528,6 +528,8 @@ DESCRIPTION .#define bfd_mach_nios2r2 2 . bfd_arch_visium, {* Visium *} .#define bfd_mach_visium 1 +. bfd_arch_pru, {* PRU *} +.#define bfd_mach_pru 0 . bfd_arch_last . }; */ @@ -631,6 +633,7 @@ extern const bfd_arch_info_type bfd_pj_arch; extern const bfd_arch_info_type bfd_plugin_arch; extern const bfd_arch_info_type bfd_powerpc_archs[]; #define bfd_powerpc_arch bfd_powerpc_archs[0] +extern const bfd_arch_info_type bfd_pru_arch; extern const bfd_arch_info_type bfd_riscv_arch; extern const bfd_arch_info_type bfd_rs6000_arch; extern const bfd_arch_info_type bfd_rl78_arch; @@ -721,6 +724,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] = &bfd_or1k_arch, &bfd_pdp11_arch, &bfd_powerpc_arch, + &bfd_pru_arch, &bfd_riscv_arch, &bfd_rl78_arch, &bfd_rs6000_arch, diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index b5ac178..9b8ed86 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2370,6 +2370,8 @@ enum bfd_architecture #define bfd_mach_nios2r2 2 bfd_arch_visium, /* Visium */ #define bfd_mach_visium 1 + bfd_arch_pru, /* PRU */ +#define bfd_mach_pru 0 bfd_arch_last }; @@ -5562,6 +5564,41 @@ a matching LO8XG part. */ BFD_RELOC_NIOS2_R2_T1X1I6, BFD_RELOC_NIOS2_R2_T1X1I6_2, +/* PRU LDI 16-bit unsigned data-memory relocation. */ + BFD_RELOC_PRU_U16, + +/* PRU LDI 16-bit unsigned instruction-memory relocation. */ + BFD_RELOC_PRU_U16_PMEMIMM, + +/* PRU relocation for two consecutive LDI load instructions that load a +32 bit value into a register. If the higher bits are all zero, then +the second instruction may be relaxed. */ + BFD_RELOC_PRU_LDI32, + +/* PRU QBBx 10-bit signed PC-relative relocation. */ + BFD_RELOC_PRU_S10_PCREL, + +/* PRU 8-bit unsigned relocation used for the LOOP instruction. */ + BFD_RELOC_PRU_U8_PCREL, + +/* PRU Program Memory relocations. Used to convert from byte addressing to +32-bit word addressing. */ + BFD_RELOC_PRU_32_PMEM, + BFD_RELOC_PRU_16_PMEM, + +/* PRU relocations to mark the difference of two local symbols. +These are only needed to support linker relaxation and can be ignored +when not relaxing. The field is set to the value of the difference +assuming no relaxation. The relocation encodes the position of the +second symbol so the linker can determine whether to adjust the field +value. The PMEM variants encode the word difference, instead of byte +difference between symbols. */ + BFD_RELOC_PRU_GNU_DIFF8, + BFD_RELOC_PRU_GNU_DIFF16, + BFD_RELOC_PRU_GNU_DIFF32, + BFD_RELOC_PRU_GNU_DIFF16_PMEM, + BFD_RELOC_PRU_GNU_DIFF32_PMEM, + /* IQ2000 Relocations. */ BFD_RELOC_IQ2000_OFFSET_16, BFD_RELOC_IQ2000_OFFSET_21, diff --git a/bfd/config.bfd b/bfd/config.bfd index 5bca5d9..07f3846 100644 --- a/bfd/config.bfd +++ b/bfd/config.bfd @@ -189,6 +189,7 @@ or1k*|or1knd*) targ_archs=bfd_or1k_arch ;; pdp11*) targ_archs=bfd_pdp11_arch ;; pj*) targ_archs="bfd_pj_arch bfd_i386_arch";; powerpc*) targ_archs="bfd_rs6000_arch bfd_powerpc_arch" ;; +pru*) targ_archs=bfd_pru_arch ;; riscv*) targ_archs=bfd_riscv_arch ;; rs6000) targ_archs="bfd_rs6000_arch bfd_powerpc_arch" ;; s390*) targ_archs=bfd_s390_arch ;; @@ -1436,6 +1437,11 @@ case "${targ}" in targ_selvecs="powerpc_pei_le_vec powerpc_pei_vec powerpc_pe_le_vec powerpc_pe_vec" ;; + pru-*-*) + targ_defvec=pru_elf32_vec + targ_underscore=yes + ;; + #ifdef BFD64 riscv32-*-*) targ_defvec=riscv_elf32_vec diff --git a/bfd/configure b/bfd/configure index 2f05cad..83256d2 100755 --- a/bfd/configure +++ b/bfd/configure @@ -14475,6 +14475,7 @@ do powerpc_pei_vec) tb="$tb pei-ppc.lo peigen.lo $coff" ;; powerpc_pei_le_vec) tb="$tb pei-ppc.lo peigen.lo $coff" ;; powerpc_xcoff_vec) tb="$tb coff-rs6000.lo $xcoff" ;; + pru_elf32_vec) tb="$tb elf32-pru.lo elf32.lo $elf" ;; riscv_elf32_vec) tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;; riscv_elf64_vec) tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;; rl78_elf32_vec) tb="$tb elf32-rl78.lo elf32.lo $elf" ;; diff --git a/bfd/configure.ac b/bfd/configure.ac index 944fc56..802aca7 100644 --- a/bfd/configure.ac +++ b/bfd/configure.ac @@ -606,6 +606,7 @@ do powerpc_pei_vec) tb="$tb pei-ppc.lo peigen.lo $coff" ;; powerpc_pei_le_vec) tb="$tb pei-ppc.lo peigen.lo $coff" ;; powerpc_xcoff_vec) tb="$tb coff-rs6000.lo $xcoff" ;; + pru_elf32_vec) tb="$tb elf32-pru.lo elf32.lo $elf" ;; riscv_elf32_vec) tb="$tb elf32-riscv.lo elfxx-riscv.lo elf32.lo $elf" ;; riscv_elf64_vec) tb="$tb elf64-riscv.lo elf64.lo elfxx-riscv.lo elf32.lo $elf"; target_size=64 ;; rl78_elf32_vec) tb="$tb elf32-rl78.lo elf32.lo $elf" ;; diff --git a/bfd/cpu-pru.c b/bfd/cpu-pru.c new file mode 100644 index 0000000..824e805 --- /dev/null +++ b/bfd/cpu-pru.c @@ -0,0 +1,43 @@ +/* BFD support for the TI PRU microprocessor. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + 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" + +#define N(BITS_WORD, BITS_ADDR, NUMBER, PRINT, DEFAULT, NEXT) \ + { \ + BITS_WORD, /* bits in a word */ \ + BITS_ADDR, /* bits in an address */ \ + 8, /* 8 bits in a byte */ \ + bfd_arch_pru, \ + NUMBER, \ + "pru", \ + PRINT, \ + 3, \ + DEFAULT, \ + bfd_default_compatible, \ + bfd_default_scan, \ + bfd_arch_default_fill, \ + NEXT \ + } + +const bfd_arch_info_type bfd_pru_arch = N (32, 32, 0, "pru", TRUE, NULL); diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h index 1c634d8..3dcc245 100644 --- a/bfd/elf-bfd.h +++ b/bfd/elf-bfd.h @@ -467,6 +467,7 @@ enum elf_target_id OR1K_ELF_DATA, PPC32_ELF_DATA, PPC64_ELF_DATA, + PRU_ELF_DATA, S390_ELF_DATA, SH_ELF_DATA, SPARC_ELF_DATA, diff --git a/bfd/elf32-pru.c b/bfd/elf32-pru.c new file mode 100644 index 0000000..b38951a --- /dev/null +++ b/bfd/elf32-pru.c @@ -0,0 +1,1469 @@ +/* 32-bit ELF support for TI PRU. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + Based on elf32-nios2.c + + 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. */ + +/* This file handles TI PRU ELF targets. */ + +#include "sysdep.h" +#include "bfd.h" +#include "libbfd.h" +#include "bfdlink.h" +#include "genlink.h" +#include "elf-bfd.h" +#include "elf/pru.h" +#include "opcode/pru.h" + +#define SWAP_VALS(A,B) \ + do { \ + (A) ^= (B); \ + (B) ^= (A); \ + (A) ^= (B); \ + } while (0) + +/* Enable debugging printout at stdout with this variable. */ +static bfd_boolean debug_relax = FALSE; + +/* Forward declarations. */ +static bfd_reloc_status_type pru_elf32_pmem_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type pru_elf32_s10_pcrel_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type pru_elf32_u8_pcrel_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type pru_elf32_ldi32_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); +static bfd_reloc_status_type bfd_elf_pru_diff_relocate + (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); + +/* Target vector. */ +extern const bfd_target pru_elf32_vec; + +/* The relocation table used for SHT_REL sections. */ +static reloc_howto_type elf_pru_howto_table_rel[] = { + /* No relocation. */ + HOWTO (R_PRU_NONE, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 3, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PRU_NONE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_16_PMEM, + 2, + 1, /* short */ + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_PRU_16_PMEM", + FALSE, + 0, /* src_mask */ + 0xffff, + FALSE), + + HOWTO (R_PRU_U16_PMEMIMM, + 2, + 2, + 32, + FALSE, + 8, + complain_overflow_unsigned, + pru_elf32_pmem_relocate, + "R_PRU_U16_PMEMIMM", + FALSE, + 0, /* src_mask */ + 0x00ffff00, + FALSE), + + HOWTO (R_PRU_BFD_RELOC_16, + 0, + 1, /* short */ + 16, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_PRU_BFD_RELOC16", + FALSE, + 0, /* src_mask */ + 0x0000ffff, + FALSE), + + /* 16-bit unsigned immediate relocation. */ + HOWTO (R_PRU_U16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 8, /* bitpos */ + complain_overflow_unsigned, /* complain on overflow */ + bfd_elf_generic_reloc, /* special function */ + "R_PRU_U16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0x00ffff00, /* dest_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_32_PMEM, + 2, + 2, /* long */ + 32, + FALSE, + 0, + complain_overflow_dont, + pru_elf32_pmem_relocate, + "R_PRU_32_PMEM", + FALSE, + 0, /* src_mask */ + 0xffffffff, + FALSE), + + HOWTO (R_PRU_BFD_RELOC_32, + 0, + 2, /* long */ + 32, + FALSE, + 0, + complain_overflow_dont, + bfd_elf_generic_reloc, + "R_PRU_BFD_RELOC32", + FALSE, + 0, /* src_mask */ + 0xffffffff, + FALSE), + + HOWTO (R_PRU_S10_PCREL, + 2, + 2, + 10, + TRUE, + 0, + complain_overflow_bitfield, + pru_elf32_s10_pcrel_relocate, + "R_PRU_S10_PCREL", + FALSE, + 0, /* src_mask */ + 0x060000ff, + TRUE), + + HOWTO (R_PRU_U8_PCREL, + 2, + 2, + 8, + TRUE, + 0, + complain_overflow_unsigned, + pru_elf32_u8_pcrel_relocate, + "R_PRU_U8_PCREL", + FALSE, + 0, /* src_mask */ + 0x000000ff, + TRUE), + + HOWTO (R_PRU_LDI32, + 0, /* rightshift */ + 4, /* size (4 = 8bytes) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain on overflow */ + pru_elf32_ldi32_relocate, /* special function */ + "R_PRU_LDI32", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffffffff, /* dest_mask */ + FALSE), /* pcrel_offset */ + + /* GNU-specific relocations. */ + HOWTO (R_PRU_GNU_BFD_RELOC_8, + 0, + 0, /* byte */ + 8, + FALSE, + 0, + complain_overflow_bitfield, + bfd_elf_generic_reloc, + "R_PRU_BFD_RELOC8", + FALSE, + 0, /* src_mask */ + 0x000000ff, + FALSE), + + HOWTO (R_PRU_GNU_DIFF8, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_pru_diff_relocate, /* special_function */ + "R_PRU_DIFF8", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_GNU_DIFF16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_pru_diff_relocate,/* special_function */ + "R_PRU_DIFF16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_GNU_DIFF32, /* 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_pru_diff_relocate,/* special_function */ + "R_PRU_DIFF32", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_GNU_DIFF16_PMEM, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_pru_diff_relocate,/* special_function */ + "R_PRU_DIFF16_PMEM", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_PRU_GNU_DIFF32_PMEM, /* 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_pru_diff_relocate,/* special_function */ + "R_PRU_DIFF32_PMEM", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + +/* Add other relocations here. */ +}; + +static unsigned char elf_code_to_howto_index[R_PRU_ILLEGAL + 1]; + +/* Return the howto for relocation RTYPE. */ +static reloc_howto_type * +lookup_howto (unsigned int rtype) +{ + static int initialized = 0; + int i; + int howto_tbl_size = (int) (sizeof (elf_pru_howto_table_rel) + / sizeof (elf_pru_howto_table_rel[0])); + + if (!initialized) + { + initialized = 1; + memset (elf_code_to_howto_index, 0xff, + sizeof (elf_code_to_howto_index)); + for (i = 0; i < howto_tbl_size; i++) + elf_code_to_howto_index[elf_pru_howto_table_rel[i].type] = i; + } + + BFD_ASSERT (rtype <= R_PRU_ILLEGAL); + i = elf_code_to_howto_index[rtype]; + if (i >= howto_tbl_size) + return 0; + return elf_pru_howto_table_rel + i; +} + +/* Map for converting BFD reloc types to PRU reloc types. */ +struct elf_reloc_map +{ + bfd_reloc_code_real_type bfd_val; + enum elf_pru_reloc_type elf_val; +}; + +static const struct elf_reloc_map pru_reloc_map[] = { + {BFD_RELOC_NONE, R_PRU_NONE}, + {BFD_RELOC_PRU_16_PMEM, R_PRU_16_PMEM}, + {BFD_RELOC_PRU_U16_PMEMIMM, R_PRU_U16_PMEMIMM}, + {BFD_RELOC_16, R_PRU_BFD_RELOC_16}, + {BFD_RELOC_PRU_U16, R_PRU_U16}, + {BFD_RELOC_PRU_32_PMEM, R_PRU_32_PMEM}, + {BFD_RELOC_32, R_PRU_BFD_RELOC_32}, + {BFD_RELOC_PRU_S10_PCREL, R_PRU_S10_PCREL}, + {BFD_RELOC_PRU_U8_PCREL, R_PRU_U8_PCREL}, + {BFD_RELOC_PRU_LDI32, R_PRU_LDI32}, + + {BFD_RELOC_8, R_PRU_GNU_BFD_RELOC_8}, + {BFD_RELOC_PRU_GNU_DIFF8, R_PRU_GNU_DIFF8}, + {BFD_RELOC_PRU_GNU_DIFF16, R_PRU_GNU_DIFF16}, + {BFD_RELOC_PRU_GNU_DIFF32, R_PRU_GNU_DIFF32}, + {BFD_RELOC_PRU_GNU_DIFF16_PMEM, R_PRU_GNU_DIFF16_PMEM}, + {BFD_RELOC_PRU_GNU_DIFF32_PMEM, R_PRU_GNU_DIFF32_PMEM}, +}; + + +/* Assorted hash table functions. */ + +/* Create an entry in a PRU ELF linker hash table. */ +static struct bfd_hash_entry * +link_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, const char *string) +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, + sizeof (struct elf_link_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = _bfd_elf_link_hash_newfunc (entry, table, string); + + return entry; +} + +/* Implement bfd_elf32_bfd_reloc_type_lookup: + Given a BFD reloc type, return a howto structure. */ +static reloc_howto_type * +pru_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, + bfd_reloc_code_real_type code) +{ + int i; + for (i = 0; + i < (int) (sizeof (pru_reloc_map) / sizeof (struct elf_reloc_map)); + ++i) + if (pru_reloc_map[i].bfd_val == code) + return lookup_howto ((unsigned int) pru_reloc_map[i].elf_val); + return NULL; +} + +/* Implement bfd_elf32_bfd_reloc_name_lookup: + Given a reloc name, return a howto structure. */ +static reloc_howto_type * +pru_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, + const char *r_name) +{ + unsigned int i; + for (i = 0; + i < (sizeof (elf_pru_howto_table_rel) + / sizeof (elf_pru_howto_table_rel[0])); + i++) + if (elf_pru_howto_table_rel[i].name + && strcasecmp (elf_pru_howto_table_rel[i].name, r_name) == 0) + return &elf_pru_howto_table_rel[i]; + + return NULL; +} + +/* Implement elf_info_to_howto: + Given a ELF32 relocation, fill in a arelent structure. */ +static void +pru_elf32_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr, + Elf_Internal_Rela *dst) +{ + unsigned int r_type; + + r_type = ELF32_R_TYPE (dst->r_info); + BFD_ASSERT (r_type < R_PRU_ILLEGAL); + cache_ptr->howto = lookup_howto (r_type); +} + +/* Do the relocations that require special handling. */ +/* Produce a word address for program memory. Linker scripts will put .text + at a high offset in order to differentiate it from .data. So here we also + mask the high bits of PMEM address. + + But why 1MB when internal Program Memory much smaller? We want to catch + unintended overflows. + + Why not use (1<<31) as an offset and a mask? Sitara DDRAM usually resides + there, and users might want to put some shared carveout memory region in + their linker scripts. So 0x80000000 might be a valid .data address. + + Note that we still keep and pass down the original howto. This way we + can reuse this function for several different relocations. */ +static bfd_reloc_status_type +pru_elf32_do_pmem_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + symbol_value = symbol_value + addend; + addend = 0; + symbol_value &= 0x3fffff; + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +/* Direct copy of _bfd_final_link_relocate, but with special + "fill-in". This copy-paste mumbo jumbo is only needed because BFD + cannot deal correctly with non-contiguous bit fields. */ +static bfd_reloc_status_type +pru_elf32_do_s10_pcrel_relocate (bfd *input_bfd, reloc_howto_type *howto, + asection *input_section, + bfd_byte *contents, bfd_vma address, + bfd_vma relocation, bfd_vma addend) +{ + bfd_byte *location; + bfd_vma x = 0; + bfd_vma qboff; + bfd_reloc_status_type flag = bfd_reloc_ok; + + /* Sanity check the address. */ + if (address > bfd_get_section_limit (input_bfd, input_section)) + return bfd_reloc_outofrange; + + BFD_ASSERT (howto->pc_relative); + BFD_ASSERT (howto->pcrel_offset); + + relocation = relocation + addend - (input_section->output_section->vma + + input_section->output_offset) - address; + + location = contents + address; + + /* Get the value we are going to relocate. */ + BFD_ASSERT (bfd_get_reloc_size (howto) == 4); + x = bfd_get_32 (input_bfd, location); + + qboff = GET_BROFF_SIGNED (x) << howto->rightshift; + relocation += qboff; + + BFD_ASSERT (howto->complain_on_overflow == complain_overflow_bitfield); + + if (relocation > 2047 && relocation < (bfd_vma)-2048l) + flag = bfd_reloc_overflow; + + /* Check that target address is word-aligned. */ + if (relocation & ((1 << howto->rightshift) - 1)) + flag = bfd_reloc_outofrange; + + relocation >>= (bfd_vma) howto->rightshift; + + /* Fill-in the RELOCATION to the right bits of X. */ + SET_BROFF_URAW (x, relocation); + + bfd_put_32 (input_bfd, x, location); + + return flag; +} + +static bfd_reloc_status_type +pru_elf32_do_u8_pcrel_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_vma relocation; + + BFD_ASSERT (howto->pc_relative); + BFD_ASSERT (howto->pcrel_offset); + + relocation = symbol_value + addend - (input_section->output_section->vma + + input_section->output_offset) - offset; + relocation >>= howto->rightshift; + + /* 0 and 1 are invalid target labels for LOOP. We cannot + encode this info in HOWTO, so catch such cases here. */ + if (relocation < 2) + return bfd_reloc_outofrange; + + return _bfd_final_link_relocate (howto, abfd, input_section, + data, offset, symbol_value, addend); +} + +/* Idea and code taken from elf32-d30v. */ +static bfd_reloc_status_type +pru_elf32_do_ldi32_relocate (bfd *abfd, reloc_howto_type *howto, + asection *input_section, + bfd_byte *data, bfd_vma offset, + bfd_vma symbol_value, bfd_vma addend) +{ + bfd_signed_vma relocation; + bfd_size_type octets = offset * bfd_octets_per_byte (abfd); + bfd_byte *location; + unsigned long in1, in2, num; + + /* A hacked-up version of _bfd_final_link_relocate() follows. */ + + /* Sanity check the address. */ + if (octets + bfd_get_reloc_size (howto) + > bfd_get_section_limit_octets (abfd, input_section)) + return bfd_reloc_outofrange; + + /* This function assumes that we are dealing with a basic relocation + against a symbol. We want to compute the value of the symbol to + relocate to. This is just VALUE, the value of the symbol, plus + ADDEND, any addend associated with the reloc. */ + relocation = symbol_value + addend; + + BFD_ASSERT (!howto->pc_relative); + + /* A hacked-up version of _bfd_relocate_contents() follows. */ + location = data + offset * bfd_octets_per_byte (abfd); + + BFD_ASSERT (!howto->pc_relative); + + in1 = bfd_get_32 (abfd, location); + in2 = bfd_get_32 (abfd, location + 4); + + /* Extract the addend - should be zero per my understanding. */ + num = GET_INSN_FIELD (IMM16, in1) | (GET_INSN_FIELD (IMM16, in2) << 16); + BFD_ASSERT (!num); + + relocation += num; + + SET_INSN_FIELD (IMM16, in1, relocation & 0xffff); + SET_INSN_FIELD (IMM16, in2, relocation >> 16); + + bfd_put_32 (abfd, in1, location); + bfd_put_32 (abfd, in2, location + 4); + + return bfd_reloc_ok; +} + +/* HOWTO handlers for relocations that require special handling. */ + +static bfd_reloc_status_type +pru_elf32_pmem_relocate (bfd *abfd, arelent *reloc_entry, + asymbol *symbol, void *data, + asection *input_section, bfd *output_bfd, + char **error_message) +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + return pru_elf32_do_pmem_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +pru_elf32_s10_pcrel_relocate (bfd *abfd, arelent *reloc_entry, + asymbol *symbol, void *data, + asection *input_section, bfd *output_bfd, + char **error_message) +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + return pru_elf32_do_s10_pcrel_relocate (abfd, reloc_entry->howto, + input_section, data, + reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +pru_elf32_u8_pcrel_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, + char **error_message) +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + return pru_elf32_do_u8_pcrel_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + +static bfd_reloc_status_type +pru_elf32_ldi32_relocate (bfd *abfd, arelent *reloc_entry, asymbol *symbol, + void *data, asection *input_section, + bfd *output_bfd, + char **error_message) +{ + /* If this is a relocatable link (output_bfd test tells us), just + call the generic function. Any adjustment will be done at final + link time. */ + if (output_bfd != NULL) + return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, + input_section, output_bfd, error_message); + + return pru_elf32_do_ldi32_relocate (abfd, reloc_entry->howto, + input_section, + data, reloc_entry->address, + (symbol->value + + symbol->section->output_section->vma + + symbol->section->output_offset), + reloc_entry->addend); +} + + +/* Implement elf_backend_relocate_section. */ +static bfd_boolean +pru_elf32_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; + + symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr; + sym_hashes = elf_sym_hashes (input_bfd); + relend = relocs + input_section->reloc_count; + + 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 = bfd_reloc_ok; + const char *name = NULL; + const char* msg = (const char*) NULL; + bfd_boolean unresolved_reloc; + + r_symndx = ELF32_R_SYM (rel->r_info); + + howto = lookup_howto ((unsigned) 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 + { + bfd_boolean 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 && discarded_section (sec)) + RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section, + rel, 1, relend, howto, 0, contents); + + /* Nothing more to do unless this is a final link. */ + if (bfd_link_relocatable (info)) + continue; + + if (howto) + { + switch (howto->type) + { + case R_PRU_NONE: + /* We don't need to find a value for this symbol. It's just a + marker. */ + r = bfd_reloc_ok; + break; + + case R_PRU_U16_PMEMIMM: + case R_PRU_32_PMEM: + case R_PRU_16_PMEM: + r = pru_elf32_do_pmem_relocate (input_bfd, howto, + input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + break; + case R_PRU_S10_PCREL: + r = pru_elf32_do_s10_pcrel_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_PRU_U8_PCREL: + r = pru_elf32_do_u8_pcrel_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_PRU_LDI32: + r = pru_elf32_do_ldi32_relocate (input_bfd, howto, + input_section, + contents, + rel->r_offset, + relocation, + rel->r_addend); + break; + case R_PRU_GNU_DIFF8: + case R_PRU_GNU_DIFF16: + case R_PRU_GNU_DIFF32: + case R_PRU_GNU_DIFF16_PMEM: + case R_PRU_GNU_DIFF32_PMEM: + /* Nothing to do here, as contents already contain the + diff value. */ + r = bfd_reloc_ok; + break; + + default: + r = _bfd_final_link_relocate (howto, input_bfd, + input_section, contents, + rel->r_offset, relocation, + rel->r_addend); + break; + } + } + else + r = bfd_reloc_notsupported; + + if (r != bfd_reloc_ok) + { + if (h != NULL) + name = h->root.root.string; + else + { + name = bfd_elf_string_from_elf_section (input_bfd, + symtab_hdr->sh_link, + sym->st_name); + if (name == NULL || *name == '\0') + name = bfd_section_name (input_bfd, sec); + } + + switch (r) + { + case bfd_reloc_overflow: + (*info->callbacks->reloc_overflow) (info, 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: + if (msg == NULL) + msg = _("relocation out of range"); + break; + + case bfd_reloc_notsupported: + if (msg == NULL) + msg = _("unsupported relocation"); + break; + + case bfd_reloc_dangerous: + if (msg == NULL) + msg = _("dangerous relocation"); + break; + + default: + if (msg == NULL) + msg = _("unknown error"); + break; + } + + if (msg) + { + (*info->callbacks->warning) (info, msg, name, input_bfd, + input_section, rel->r_offset); + return FALSE; + } + } + } + return TRUE; +} + + +/* Perform a diff relocation. Nothing to do, as the difference value is + already written into the section's contents. */ + +static bfd_reloc_status_type +bfd_elf_pru_diff_relocate (bfd *abfd ATTRIBUTE_UNUSED, + arelent *reloc_entry ATTRIBUTE_UNUSED, + asymbol *symbol ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED, + asection *input_section ATTRIBUTE_UNUSED, + bfd *output_bfd ATTRIBUTE_UNUSED, + char **error_message ATTRIBUTE_UNUSED) +{ + return bfd_reloc_ok; +} + + +/* Returns whether the relocation type passed is a diff reloc. */ + +static bfd_boolean +elf32_pru_is_diff_reloc (Elf_Internal_Rela *irel) +{ + return (ELF32_R_TYPE (irel->r_info) == R_PRU_GNU_DIFF8 + || ELF32_R_TYPE (irel->r_info) == R_PRU_GNU_DIFF16 + || ELF32_R_TYPE (irel->r_info) == R_PRU_GNU_DIFF32 + || ELF32_R_TYPE (irel->r_info) == R_PRU_GNU_DIFF16_PMEM + || ELF32_R_TYPE (irel->r_info) == R_PRU_GNU_DIFF32_PMEM); +} + +/* Reduce the diff value written in the section by count if the shrinked + insn address happens to fall between the two symbols for which this + diff reloc was emitted. */ + +static void +elf32_pru_adjust_diff_reloc_value (bfd *abfd, + struct bfd_section *isec, + Elf_Internal_Rela *irel, + bfd_vma symval, + bfd_vma shrinked_insn_address, + int count) +{ + unsigned char *reloc_contents = NULL; + unsigned char *isec_contents = elf_section_data (isec)->this_hdr.contents; + if (isec_contents == NULL) + { + if (! bfd_malloc_and_get_section (abfd, isec, &isec_contents)) + return; + + elf_section_data (isec)->this_hdr.contents = isec_contents; + } + + reloc_contents = isec_contents + irel->r_offset; + + /* Read value written in object file. */ + bfd_signed_vma x = 0; + switch (ELF32_R_TYPE (irel->r_info)) + { + case R_PRU_GNU_DIFF8: + { + x = bfd_get_signed_8 (abfd, reloc_contents); + break; + } + case R_PRU_GNU_DIFF16: + { + x = bfd_get_signed_16 (abfd, reloc_contents); + break; + } + case R_PRU_GNU_DIFF32: + { + x = bfd_get_signed_32 (abfd, reloc_contents); + break; + } + case R_PRU_GNU_DIFF16_PMEM: + { + x = bfd_get_signed_16 (abfd, reloc_contents) * 4; + break; + } + case R_PRU_GNU_DIFF32_PMEM: + { + x = bfd_get_signed_32 (abfd, reloc_contents) * 4; + break; + } + default: + { + BFD_FAIL (); + } + } + + /* For a diff reloc sym1 - sym2 the diff at assembly time (x) is written + into the object file at the reloc offset. sym2's logical value is + symval () + reloc addend. Compute the start and end + addresses and check if the shrinked insn falls between sym1 and sym2. */ + + bfd_vma end_address = symval + irel->r_addend; + bfd_vma start_address = end_address - x; + + /* Shrink the absolute DIFF value (get the to labels "closer" + together), because we have removed data between labels. */ + if (x < 0) + { + x += count; + /* In case the signed x is negative, restore order. */ + SWAP_VALS (end_address, start_address); + } + else + { + x -= count; + } + + /* Reduce the diff value by count bytes and write it back into section + contents. */ + + if (shrinked_insn_address >= start_address + && shrinked_insn_address <= end_address) + { + switch (ELF32_R_TYPE (irel->r_info)) + { + case R_PRU_GNU_DIFF8: + { + bfd_put_signed_8 (abfd, x & 0xFF, reloc_contents); + break; + } + case R_PRU_GNU_DIFF16: + { + bfd_put_signed_16 (abfd, x & 0xFFFF, reloc_contents); + break; + } + case R_PRU_GNU_DIFF32: + { + bfd_put_signed_32 (abfd, x & 0xFFFFFFFF, reloc_contents); + break; + } + case R_PRU_GNU_DIFF16_PMEM: + { + bfd_put_signed_16 (abfd, (x / 4) & 0xFFFF, reloc_contents); + break; + } + case R_PRU_GNU_DIFF32_PMEM: + { + bfd_put_signed_32 (abfd, (x / 4) & 0xFFFFFFFF, reloc_contents); + break; + } + default: + { + BFD_FAIL (); + } + } + + } +} + +/* Delete some bytes from a section while changing the size of an instruction. + The parameter "addr" denotes the section-relative offset pointing just + behind the shrinked instruction. "addr+count" point at the first + byte just behind the original unshrinked instruction. + + Idea copied from the AVR port. */ + +static bfd_boolean +pru_elf_relax_delete_bytes (bfd *abfd, + asection *sec, + bfd_vma addr, + int count) +{ + Elf_Internal_Shdr *symtab_hdr; + unsigned int sec_shndx; + bfd_byte *contents; + Elf_Internal_Rela *irel, *irelend; + Elf_Internal_Sym *isym; + Elf_Internal_Sym *isymbuf = NULL; + bfd_vma toaddr; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + contents = elf_section_data (sec)->this_hdr.contents; + + toaddr = sec->size; + + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + + /* Actually delete the bytes. */ + if (toaddr - addr - count > 0) + memmove (contents + addr, contents + addr + count, + (size_t) (toaddr - addr - count)); + sec->size -= count; + + /* Adjust all the reloc addresses. */ + for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + { + bfd_vma old_reloc_address; + + old_reloc_address = (sec->output_section->vma + + sec->output_offset + irel->r_offset); + + /* Get the new reloc address. */ + if ((irel->r_offset > addr + && irel->r_offset < toaddr)) + { + if (debug_relax) + printf ("Relocation at address 0x%x needs to be moved.\n" + "Old section offset: 0x%x, New section offset: 0x%x \n", + (unsigned int) old_reloc_address, + (unsigned int) irel->r_offset, + (unsigned int) ((irel->r_offset) - count)); + + irel->r_offset -= count; + } + + } + + /* The reloc's own addresses are now ok. However, we need to readjust + the reloc's addend, i.e. the reloc's value if two conditions are met: + 1.) the reloc is relative to a symbol in this section that + is located in front of the shrinked instruction + 2.) symbol plus addend end up behind the shrinked instruction. + + The most common case where this happens are relocs relative to + the section-start symbol. + + This step needs to be done for all of the sections of the bfd. */ + + { + struct bfd_section *isec; + + for (isec = abfd->sections; isec; isec = isec->next) + { + bfd_vma symval; + bfd_vma shrinked_insn_address; + + if (isec->reloc_count == 0) + continue; + + shrinked_insn_address = (sec->output_section->vma + + sec->output_offset + addr - count); + + irel = elf_section_data (isec)->relocs; + /* PR 12161: Read in the relocs for this section if necessary. */ + if (irel == NULL) + irel = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL, TRUE); + + for (irelend = irel + isec->reloc_count; + irel < irelend; + irel++) + { + /* Read this BFD's local symbols if we haven't done + so already. */ + if (isymbuf == NULL && symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + return FALSE; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = isym->st_value; + /* If the reloc is absolute, it will not have + a symbol or section associated with it. */ + if (sym_sec == sec) + { + symval += sym_sec->output_section->vma + + sym_sec->output_offset; + + if (debug_relax) + printf ("Checking if the relocation's " + "addend needs corrections.\n" + "Address of anchor symbol: 0x%x \n" + "Address of relocation target: 0x%x \n" + "Address of relaxed insn: 0x%x \n", + (unsigned int) symval, + (unsigned int) (symval + irel->r_addend), + (unsigned int) shrinked_insn_address); + + /* Shrink the special DIFF relocations. */ + if (elf32_pru_is_diff_reloc (irel)) + { + elf32_pru_adjust_diff_reloc_value (abfd, isec, irel, + symval, + shrinked_insn_address, + count); + } + + /* Fix the addend, if it is affected. */ + if (symval <= shrinked_insn_address + && (symval + irel->r_addend) > shrinked_insn_address) + { + + irel->r_addend -= count; + + if (debug_relax) + printf ("Relocation's addend needed to be fixed \n"); + } + } + /* else...Reference symbol is absolute. + No adjustment needed. */ + } + /* else...Reference symbol is extern. No need for adjusting + the addend. */ + } + } + } + + /* Adjust the local symbols defined in this section. */ + isym = (Elf_Internal_Sym *) symtab_hdr->contents; + /* Fix PR 9841, there may be no local symbols. */ + if (isym != NULL) + { + Elf_Internal_Sym *isymend; + + isymend = isym + symtab_hdr->sh_info; + for (; isym < isymend; isym++) + { + if (isym->st_shndx == sec_shndx) + { + if (isym->st_value > addr + && isym->st_value <= toaddr) + isym->st_value -= count; + + if (isym->st_value <= addr + && isym->st_value + isym->st_size > addr) + { + /* If this assert fires then we have a symbol that ends + part way through an instruction. Does that make + sense? */ + BFD_ASSERT (isym->st_value + isym->st_size >= addr + count); + 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) + { + if (sym_hash->root.u.def.value > addr + && sym_hash->root.u.def.value <= toaddr) + sym_hash->root.u.def.value -= count; + + if (sym_hash->root.u.def.value <= addr + && (sym_hash->root.u.def.value + sym_hash->size > addr)) + { + /* If this assert fires then we have a symbol that ends + part way through an instruction. Does that make + sense? */ + BFD_ASSERT (sym_hash->root.u.def.value + sym_hash->size + >= addr + count); + sym_hash->size -= count; + } + } + } + + return TRUE; +} + +static bfd_boolean +pru_elf32_relax_section (bfd * abfd, asection * sec, + struct bfd_link_info * link_info, + bfd_boolean * again) +{ + Elf_Internal_Shdr * symtab_hdr; + Elf_Internal_Rela * internal_relocs; + Elf_Internal_Rela * irel; + Elf_Internal_Rela * irelend; + bfd_byte * contents = NULL; + Elf_Internal_Sym * isymbuf = NULL; + + /* 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_tdata (abfd)->symtab_hdr; + + /* Get a copy of the native relocations. */ + internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL, + link_info->keep_memory); + if (internal_relocs == NULL) + goto error_return; + + /* Walk through them looking for relaxing opportunities. */ + irelend = internal_relocs + sec->reloc_count; + + for (irel = internal_relocs; irel < irelend; irel++) + { + bfd_vma symval; + + /* Get the section contents if we haven't done so already. */ + if (contents == NULL) + { + /* Get cached copy if it exists. */ + if (elf_section_data (sec)->this_hdr.contents != NULL) + contents = elf_section_data (sec)->this_hdr.contents; + else if (! bfd_malloc_and_get_section (abfd, sec, &contents)) + goto error_return; + } + + /* Read this BFD's local symbols if we haven't done so already. */ + if (isymbuf == NULL && symtab_hdr->sh_info != 0) + { + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + goto error_return; + } + + /* Get the value of the symbol referred to by the reloc. */ + if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info) + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + asection *sym_sec; + + isym = isymbuf + ELF32_R_SYM (irel->r_info); + if (isym->st_shndx == SHN_UNDEF) + sym_sec = bfd_und_section_ptr; + else if (isym->st_shndx == SHN_ABS) + sym_sec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + sym_sec = bfd_com_section_ptr; + else + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + symval = (isym->st_value + + sym_sec->output_section->vma + sym_sec->output_offset); + } + else + { + unsigned long indx; + struct elf_link_hash_entry *h; + + /* An external symbol. */ + indx = ELF32_R_SYM (irel->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. */ + continue; + + symval = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + + /* For simplicity of coding, we are going to modify the section + contents, the section relocs, and the BFD symbol table. We + must tell the rest of the code not to free up this + information. It would be possible to instead create a table + of changes which have to be made, as is done in coff-mips.c; + that would be more work, but would require less memory when + the linker is run. */ + + /* Check if we can remove an LDI instruction from the LDI32 + pseudo instruction if the upper 16 operand bits are zero. */ + if (ELF32_R_TYPE (irel->r_info) == (int) R_PRU_LDI32) + { + bfd_vma value = symval + irel->r_addend; + + if (debug_relax) + printf ("R_PRU_LDI32 with value=0x%lx\n", (long) value); + + if ((long) value >> 16 == 0) + { + /* Note that we've changed the relocs, section contents. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Delete bytes. */ + if (!pru_elf_relax_delete_bytes (abfd, sec, irel->r_offset + 4, 4)) + goto error_return; + + /* We're done with deletion of the second instruction. + Set a regular LDI relocation for the first instruction + we left to load the 16-bit value into the 32-bit + register. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_PRU_U16); + + /* That will change things, so, we should relax again. + Note that this is not required, and it may be slow. */ + *again = TRUE; + } + } + } + + if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) + { + if (!link_info->keep_memory) + free (isymbuf); + else + { + /* Cache the symbols for elf_link_input_bfd. */ + symtab_hdr->contents = (unsigned char *) isymbuf; + } + } + + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + { + if (!link_info->keep_memory) + free (contents); + else + { + /* Cache the section contents for elf_link_input_bfd. */ + elf_section_data (sec)->this_hdr.contents = contents; + } + } + + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + return TRUE; + +error_return: + if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) + free (isymbuf); + if (contents != NULL + && elf_section_data (sec)->this_hdr.contents != contents) + free (contents); + if (internal_relocs != NULL + && elf_section_data (sec)->relocs != internal_relocs) + free (internal_relocs); + + return FALSE; +} + +/* Free the derived linker hash table. */ +static void +pru_elf32_link_hash_table_free (bfd *obfd) +{ + _bfd_elf_link_hash_table_free (obfd); +} + +/* Implement bfd_elf32_bfd_link_hash_table_create. */ +static struct bfd_link_hash_table * +pru_elf32_link_hash_table_create (bfd *abfd) +{ + struct elf_link_hash_table *ret; + bfd_size_type amt = sizeof (struct elf_link_hash_table); + + ret = bfd_zmalloc (amt); + if (ret == NULL) + return NULL; + + if (!_bfd_elf_link_hash_table_init (ret, abfd, + link_hash_newfunc, + sizeof (struct + elf_link_hash_entry), + PRU_ELF_DATA)) + { + free (ret); + return NULL; + } + + ret->root.hash_table_free = pru_elf32_link_hash_table_free; + + return &ret->root; +} + +#define ELF_ARCH bfd_arch_pru +#define ELF_TARGET_ID PRU_ELF_DATA +#define ELF_MACHINE_CODE EM_TI_PRU + +#define ELF_MAXPAGESIZE 1 + +#define bfd_elf32_bfd_link_hash_table_create \ + pru_elf32_link_hash_table_create + +/* Relocation table lookup macros. */ + +#define bfd_elf32_bfd_reloc_type_lookup pru_elf32_bfd_reloc_type_lookup +#define bfd_elf32_bfd_reloc_name_lookup pru_elf32_bfd_reloc_name_lookup + +/* elf_info_to_howto (using RELA relocations). */ + +#define elf_info_to_howto pru_elf32_info_to_howto + +/* elf backend functions. */ + +#define elf_backend_rela_normal 1 + +#define elf_backend_relocate_section pru_elf32_relocate_section +#define bfd_elf32_bfd_relax_section pru_elf32_relax_section + +#define TARGET_LITTLE_SYM pru_elf32_vec +#define TARGET_LITTLE_NAME "elf32-pru" + +#include "elf32-target.h" diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 30be5dd..5999527 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2741,6 +2741,18 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_NIOS2_R2_L5I4X1", "BFD_RELOC_NIOS2_R2_T1X1I6", "BFD_RELOC_NIOS2_R2_T1X1I6_2", + "BFD_RELOC_PRU_U16", + "BFD_RELOC_PRU_U16_PMEMIMM", + "BFD_RELOC_PRU_LDI32", + "BFD_RELOC_PRU_S10_PCREL", + "BFD_RELOC_PRU_U8_PCREL", + "BFD_RELOC_PRU_32_PMEM", + "BFD_RELOC_PRU_16_PMEM", + "BFD_RELOC_PRU_GNU_DIFF8", + "BFD_RELOC_PRU_GNU_DIFF16", + "BFD_RELOC_PRU_GNU_DIFF32", + "BFD_RELOC_PRU_GNU_DIFF16_PMEM", + "BFD_RELOC_PRU_GNU_DIFF32_PMEM", "BFD_RELOC_IQ2000_OFFSET_16", "BFD_RELOC_IQ2000_OFFSET_21", "BFD_RELOC_IQ2000_UHI16", diff --git a/bfd/po/SRC-POTFILES.in b/bfd/po/SRC-POTFILES.in index 8fcf04d..7ecdb0a 100644 --- a/bfd/po/SRC-POTFILES.in +++ b/bfd/po/SRC-POTFILES.in @@ -119,6 +119,7 @@ cpu-pdp11.c cpu-pj.c cpu-plugin.c cpu-powerpc.c +cpu-pru.c cpu-riscv.c cpu-rl78.c cpu-rs6000.c @@ -212,6 +213,7 @@ elf32-nios2.c elf32-or1k.c elf32-pj.c elf32-ppc.c +elf32-pru.c elf32-rl78.c elf32-rx.c elf32-s390.c diff --git a/bfd/reloc.c b/bfd/reloc.c index 3c7b606..4a79a23 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -6517,6 +6517,54 @@ ENUMDOC Relocations used by the Altera Nios II core. ENUM + BFD_RELOC_PRU_U16 +ENUMDOC + PRU LDI 16-bit unsigned data-memory relocation. +ENUM + BFD_RELOC_PRU_U16_PMEMIMM +ENUMDOC + PRU LDI 16-bit unsigned instruction-memory relocation. +ENUM + BFD_RELOC_PRU_LDI32 +ENUMDOC + PRU relocation for two consecutive LDI load instructions that load a + 32 bit value into a register. If the higher bits are all zero, then + the second instruction may be relaxed. +ENUM + BFD_RELOC_PRU_S10_PCREL +ENUMDOC + PRU QBBx 10-bit signed PC-relative relocation. +ENUM + BFD_RELOC_PRU_U8_PCREL +ENUMDOC + PRU 8-bit unsigned relocation used for the LOOP instruction. +ENUM + BFD_RELOC_PRU_32_PMEM +ENUMX + BFD_RELOC_PRU_16_PMEM +ENUMDOC + PRU Program Memory relocations. Used to convert from byte addressing to + 32-bit word addressing. +ENUM + BFD_RELOC_PRU_GNU_DIFF8 +ENUMX + BFD_RELOC_PRU_GNU_DIFF16 +ENUMX + BFD_RELOC_PRU_GNU_DIFF32 +ENUMX + BFD_RELOC_PRU_GNU_DIFF16_PMEM +ENUMX + BFD_RELOC_PRU_GNU_DIFF32_PMEM +ENUMDOC + PRU relocations to mark the difference of two local symbols. + These are only needed to support linker relaxation and can be ignored + when not relaxing. The field is set to the value of the difference + assuming no relaxation. The relocation encodes the position of the + second symbol so the linker can determine whether to adjust the field + value. The PMEM variants encode the word difference, instead of byte + difference between symbols. + +ENUM BFD_RELOC_IQ2000_OFFSET_16 ENUMX BFD_RELOC_IQ2000_OFFSET_21 diff --git a/bfd/targets.c b/bfd/targets.c index 8e6158e..7ae8eb4 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -799,6 +799,7 @@ extern const bfd_target powerpc_pe_le_vec; extern const bfd_target powerpc_pei_vec; extern const bfd_target powerpc_pei_le_vec; extern const bfd_target powerpc_xcoff_vec; +extern const bfd_target pru_elf32_vec; extern const bfd_target riscv_elf32_vec; extern const bfd_target riscv_elf64_vec; extern const bfd_target rl78_elf32_vec; @@ -1305,6 +1306,8 @@ static const bfd_target * const _bfd_target_vector[] = &powerpc_xcoff_vec, #endif + &pru_elf32_vec, + #ifdef BFD64 &riscv_elf32_vec, &riscv_elf64_vec, diff --git a/include/ChangeLog b/include/ChangeLog index 99d747f..42cf3d8 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,10 @@ +2016-12-31 Dimitar Dimitrov + + * elf/common.h: Add PRU ELF. + * elf/pru.h: New file. + * opcode/pru.h: New file. + * dis-asm.h (print_insn_pru): Declare. + 2016-12-23 Maciej W. Rozycki * opcode/mips.h: Document `0', `1', `2', `3', `4' and `s' diff --git a/include/dis-asm.h b/include/dis-asm.h index 2cefff4..91084f0 100644 --- a/include/dis-asm.h +++ b/include/dis-asm.h @@ -289,6 +289,7 @@ extern int print_insn_ns32k (bfd_vma, disassemble_info *); extern int print_insn_or1k (bfd_vma, disassemble_info *); extern int print_insn_pdp11 (bfd_vma, disassemble_info *); extern int print_insn_pj (bfd_vma, disassemble_info *); +extern int print_insn_pru (bfd_vma, disassemble_info *); extern int print_insn_rs6000 (bfd_vma, disassemble_info *); extern int print_insn_s390 (bfd_vma, disassemble_info *); extern int print_insn_sh (bfd_vma, disassemble_info *); diff --git a/include/elf/common.h b/include/elf/common.h index da79613..192c585 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -248,7 +248,7 @@ #define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP family */ #define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP family */ #define EM_res143 143 /* Reserved */ -#define EM_res144 144 /* Reserved */ +#define EM_TI_PRU 144 /* Texas Instruments Programmable Realtime Unit */ #define EM_res145 145 /* Reserved */ #define EM_res146 146 /* Reserved */ #define EM_res147 147 /* Reserved */ diff --git a/include/elf/pru.h b/include/elf/pru.h new file mode 100644 index 0000000..8450156 --- /dev/null +++ b/include/elf/pru.h @@ -0,0 +1,55 @@ +/* TI PRU ELF support for BFD. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + 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. */ + + +/* This file holds definitions specific to the TI PRU ELF ABI. Note + that most of this is not actually implemented by BFD. */ + +#ifndef _ELF_PRU_H +#define _ELF_PRU_H + +#include "elf/reloc-macros.h" + +START_RELOC_NUMBERS (elf_pru_reloc_type) + RELOC_NUMBER (R_PRU_NONE, 0) + RELOC_NUMBER (R_PRU_16_PMEM, 5) + RELOC_NUMBER (R_PRU_U16_PMEMIMM, 6) + RELOC_NUMBER (R_PRU_BFD_RELOC_16, 8) + RELOC_NUMBER (R_PRU_U16, 9) + RELOC_NUMBER (R_PRU_32_PMEM, 10) + RELOC_NUMBER (R_PRU_BFD_RELOC_32, 11) + RELOC_NUMBER (R_PRU_S10_PCREL, 14) + RELOC_NUMBER (R_PRU_U8_PCREL, 15) + RELOC_NUMBER (R_PRU_LDI32, 18) + + /* Extensions required by GCC, or simply nice to have. */ + RELOC_NUMBER (R_PRU_GNU_BFD_RELOC_8, 64) + RELOC_NUMBER (R_PRU_GNU_DIFF8, 65) + RELOC_NUMBER (R_PRU_GNU_DIFF16, 66) + RELOC_NUMBER (R_PRU_GNU_DIFF32, 67) + RELOC_NUMBER (R_PRU_GNU_DIFF16_PMEM, 68) + RELOC_NUMBER (R_PRU_GNU_DIFF32_PMEM, 69) + RELOC_NUMBER (R_PRU_ILLEGAL, 70) +END_RELOC_NUMBERS (R_PRU_maxext) + +/* Processor-specific section flags. */ + +#endif /* _ELF_PRU_H */ diff --git a/include/opcode/pru.h b/include/opcode/pru.h new file mode 100644 index 0000000..7ab6ee7 --- /dev/null +++ b/include/opcode/pru.h @@ -0,0 +1,411 @@ +/* TI PRU opcode list for GAS, the GNU assembler. + Copyright (C) 2014-2016 Free Software Foundation, Inc. + Contributed by Dimitar Dimitrov + + This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler. + + GAS/GDB 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, or (at your option) + any later version. + + GAS/GDB 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 GAS or GDB; see the file COPYING3. If not, write to + the Free Software Foundation, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef _PRU_H_ +#define _PRU_H_ + +#include "bfd.h" + +/**************************************************************************** + * This file contains structures, bit masks and shift counts used + * by the GNU toolchain to define the PRU instruction set and + * access various opcode fields. + ****************************************************************************/ + +/* Identify different overflow situations for error messages. */ +enum overflow_type +{ + call_target_overflow = 0, + qbranch_target_overflow, + address_offset_overflow, + signed_immed16_overflow, + unsigned_immed32_overflow, + unsigned_immed16_overflow, + unsigned_immed8_overflow, + unsigned_immed5_overflow, + no_overflow +}; + +enum opcode_format_type { + opcode_format1, + opcode_format2ab, + opcode_format2abl, + opcode_format2c, + opcode_format2de, + opcode_format45, + opcode_format6 +}; + +/* Opcode ID listing. Used for indexing by the simulator. */ +enum pru_instr_type { + prui_add, prui_adc, prui_sub, prui_suc, prui_lsl, prui_lsr, prui_rsb, + prui_rsc, prui_and, prui_or, prui_xor, prui_min, prui_max, prui_clr, + prui_set, prui_not, prui_jmp, prui_jal, prui_ldi, prui_halt, prui_slp, + prui_xin, prui_xout, prui_xchg, prui_sxin, prui_sxout, prui_sxchg, + prui_loop, prui_iloop, prui_qbgt, prui_qbge, prui_qblt, prui_qble, + prui_qbeq, prui_qbne, prui_qba, prui_qbbs, prui_qbbc, prui_lbbo, + prui_sbbo, prui_lbco, prui_sbco +}; + +/* This structure holds information for a particular instruction. + + The args field is a string describing the operands. The following + letters can appear in the args: + b - a 5.3-bit right source register index OR 8-bit unsigned immediate + B - same as 'b', but for LOOP instruction where IMM is decremented + c - a 5 bit unsigned immediate for constant table offset + d - a 5.3-bit destination register index + D - a 5.2-bit destination register index + E - for internal GAS self-tests only + i - a 32-bit immediate or label + j - a 5.3-bit right source register index OR 18-bit PC address + l - burst length (unsigned 7-bit immediate or r0.b[0-3]) for xLBCO + n - burst length (unsigned 7-bit immediate or r0.b[0-3]) for XFR + o - a 10-bit signed PC-relative offset + O - an 8-bit unsigned PC-relative offset for LOOP termination point + R - a 5-bit destination register index + s - a 5.3-bit left source register index + S - a 5-bit left source register index + w - a single bit for "WakeOnStatus" + W - a 16-bit unsigned immediate with IO=0 field (LDI) + x - an 8-bit XFR wide-bus address immediate + Literal ',' character may also appear in the args as delimiter. + + Most of the macro names are from [1]. + + The pinfo field is INSN_MACRO for a macro. Otherwise, it is a collection + of bits describing the instruction, notably any relevant hazard + information. + + When assembling, the match field contains the opcode template, which + is modified by the arguments to produce the actual opcode + that is emitted. If pinfo is INSN_MACRO, then this is 0. + + If pinfo is INSN_MACRO, the mask field stores the macro identifier. + Otherwise this is a bit mask for the relevant portions of the opcode + when disassembling. If the actual opcode anded with the match field + equals the opcode field, then we have found the correct instruction. + + [1] http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit */ + +struct pru_opcode +{ + const char *name; /* The name of the instruction. */ + enum pru_instr_type type; /* Instruction type. Used for fast indexing + by the simulator. */ + const char *args; /* A string describing the arguments for this + instruction. */ + unsigned long match; /* The basic opcode for the instruction. */ + unsigned long mask; /* Mask for the opcode field of the + instruction. */ + unsigned long pinfo; /* Is this a real instruction or instruction + macro? */ + enum overflow_type overflow_msg; /* Used to generate informative + message when fixup overflows. */ +}; + +/* This value is used in the pru_opcode.pinfo field to indicate that the + instruction is a macro or pseudo-op. This requires special treatment by + the assembler, and is used by the disassembler to determine whether to + check for a nop. */ +#define PRU_INSN_MACRO 0x80000000 + +/* This macro is specially handled throughout the code because it is + the only insn to output 2 words (64 bits). */ +#define PRU_INSN_LDI32 0x40000000 + +/* Associates a register name with a 5-bit index and 3-bit regsel. */ +struct pru_reg +{ + const char *name; /* Name, e.g. "r10". */ + const unsigned int index; /* Index, e.g. 10. */ + const unsigned int regsel; /* Register field selector, .e.g RSEL_31_0. */ +}; + +/* Macros for getting and setting an instruction field. */ +#define GET_INSN_FIELD(X, i) \ + (((i) & OP_MASK_##X) >> OP_SH_##X) +#define SET_INSN_FIELD(X, i, v) \ + ((i) = (((i) & ~OP_MASK_##X) | (((v) << OP_SH_##X) & OP_MASK_##X))) + +#define CHECK_INSN_FIELD(X, i) \ + (((i) & OP_MASK_##X) == OP_MATCH_##X) + +/* Masks, values, shifts and macros for accessing the various opcode fields. */ + +#define OP_SH_FMT1_OP 29 +#define OP_MASK_FMT1_OP (0x7u << 29) +#define OP_MATCH_FMT1_OP (0x0u << 29) + +#define OP_SH_FMT2_OP 29 +#define OP_MASK_FMT2_OP (0x7u << 29) +#define OP_MATCH_FMT2_OP (0x1u << 29) + +#define OP_SH_FMT4_OP 30 +#define OP_MASK_FMT4_OP (0x3u << 30) +#define OP_MATCH_FMT4_OP (0x1u << 30) + +#define OP_SH_FMT5_OP 29 +#define OP_MASK_FMT5_OP (0x7u << 29) +#define OP_MATCH_FMT5_OP (0x6u << 29) + +#define OP_SH_FMT6AB_OP 29 +#define OP_MASK_FMT6AB_OP (0x7u << 29) +#define OP_MATCH_FMT6AB_OP (0x7u << 29) + +#define OP_SH_FMT6CD_OP 29 +#define OP_MASK_FMT6CD_OP (0x7u << 29) +#define OP_MATCH_FMT6CD_OP (0x4u << 29) + +/* Generic fields. */ +#define OP_SH_SUBOP 25 +#define OP_MASK_SUBOP (0xfu << 25) + +#define OP_SH_IO 24 +#define OP_MASK_IO (0x1u << 24) + +#define OP_SH_RS2SEL 21 +#define OP_MASK_RS2SEL (0x7u << 21) +#define OP_SH_RS2 16 +#define OP_MASK_RS2 (0x1fu << 16) +#define OP_SH_RS1SEL 13 +#define OP_MASK_RS1SEL (0x7u << 13) +#define OP_SH_RS1 8 +#define OP_MASK_RS1 (0x1fu << 8) +#define OP_SH_RDSEL 5 +#define OP_MASK_RDSEL (0x7u << 5) +#define OP_SH_RD 0 +#define OP_MASK_RD (0x1fu << 0) +#define OP_SH_IMM8 16 +#define OP_MASK_IMM8 (0xffu << 16) +#define OP_SH_IMM16 8 +#define OP_MASK_IMM16 (0xffffu << 8) + +#define RSEL_7_0 0u +#define RSEL_15_8 1u +#define RSEL_23_16 2u +#define RSEL_31_24 3u +#define RSEL_15_0 4u +#define RSEL_23_8 5u +#define RSEL_31_16 6u +#define RSEL_31_0 7u +#define RSEL_NUM_ITEMS 8u + +/* Format 1 specific fields. */ +#define SUBOP_ADD 0u +#define SUBOP_ADC 1u +#define SUBOP_SUB 2u +#define SUBOP_SUC 3u +#define SUBOP_LSL 4u +#define SUBOP_LSR 5u +#define SUBOP_RSB 6u +#define SUBOP_RSC 7u +#define SUBOP_AND 8u +#define SUBOP_OR 9u +#define SUBOP_XOR 10u +#define SUBOP_NOT 11u +#define SUBOP_MIN 12u +#define SUBOP_MAX 13u +#define SUBOP_CLR 14u +#define SUBOP_SET 15u + +/* Format 2 specific fields. */ +#define SUBOP_JMP 0u +#define SUBOP_JAL 1u +#define SUBOP_LDI 2u +#define SUBOP_LMBD 3u +#define SUBOP_SCAN 4u +#define SUBOP_HALT 5u +#define SUBOP_RSVD_FOR_MVIx 6u +#define SUBOP_XFR 7u +#define SUBOP_LOOP 8u +#define SUBOP_RSVD_FOR_RFI 14u +#define SUBOP_SLP 15u + +#define OP_SH_WAKEONSTATUS 23 +#define OP_MASK_WAKEONSTATUS (0x1u << 23) + +/* Format 2 XFR specific fields. */ +#define OP_SH_SUBOP_XFR 23 +#define OP_MASK_SUBOP_XFR (3u << 23) +#define OP_SH_XFR_WBA 15 +#define OP_MASK_XFR_WBA (0xffu << 15) +#define OP_SH_XFR_S 14 +#define OP_MASK_XFR_S (1u << 14) +#define OP_SH_XFR_LENGTH 7 +#define OP_MASK_XFR_LENGTH (0x7fu << 7) + +#define SUBOP_XFR_XIN 1u +#define SUBOP_XFR_XOUT 2u +#define SUBOP_XFR_XCHG 3u + +/* Format 2 LOOP specific fields. */ +#define OP_SH_LOOP_INTERRUPTIBLE 15 +#define OP_MASK_LOOP_INTERRUPTIBLE (1u << 15) +#define OP_SH_LOOP_JMPOFFS 0 +#define OP_MASK_LOOP_JMPOFFS (0xffu << 0) + +/* Format 4 specific fields. */ +#define OP_SH_BROFF98 25 +#define OP_MASK_BROFF98 (0x3u << 25) +#define OP_SH_BROFF70 0 +#define OP_MASK_BROFF70 (0xffu << 0) +#define OP_SH_GT 29 +#define OP_MASK_GT (0x1u << 29) +#define OP_SH_EQ 28 +#define OP_MASK_EQ (0x1u << 28) +#define OP_SH_LT 27 +#define OP_MASK_LT (0x1u << 27) +#define OP_MASK_CMP (OP_MASK_GT | OP_MASK_EQ | OP_MASK_LT) + + +/* Format 5 specific fields. */ +#define OP_SH_BS 28 +#define OP_MASK_BS (0x1u << 28) +#define OP_SH_BC 27 +#define OP_MASK_BC (0x1u << 27) +#define OP_MASK_BCMP (OP_MASK_BS | OP_MASK_BC) + +/* Format 6 specific fields. */ +#define OP_SH_LOADSTORE 28 +#define OP_MASK_LOADSTORE (0x1u << 28) +#define OP_SH_BURSTLEN64 25 +#define OP_MASK_BURSTLEN64 (0x7u << 25) +#define OP_SH_BURSTLEN31 13 +#define OP_MASK_BURSTLEN31 (0x7u << 13) +#define OP_SH_CB 8 +#define OP_MASK_CB (0x1fu << 8) +#define OP_SH_BURSTLEN0 7 +#define OP_MASK_BURSTLEN0 (0x1u << 7) +#define OP_SH_RDB 5 +#define OP_MASK_RDB (0x3u << 5) + +#define LSSBBO_BYTECOUNT_R0_BITS7_0 124u +#define LSBBO_BYTECOUNT_R0_BITS15_8 125u +#define LSBBO_BYTECOUNT_R0_BITS23_16 126u +#define LSBBO_BYTECOUNT_R0_BITS31_24 127u + +/* The following macros define the opcode matches for each + instruction code & OP_MASK_INST == OP_MATCH_INST. */ +#define OP_MATCH_ADD (OP_MATCH_FMT1_OP | (SUBOP_ADD << OP_SH_SUBOP)) +#define OP_MATCH_ADC (OP_MATCH_FMT1_OP | (SUBOP_ADC << OP_SH_SUBOP)) +#define OP_MATCH_SUB (OP_MATCH_FMT1_OP | (SUBOP_SUB << OP_SH_SUBOP)) +#define OP_MATCH_SUC (OP_MATCH_FMT1_OP | (SUBOP_SUC << OP_SH_SUBOP)) +#define OP_MATCH_LSL (OP_MATCH_FMT1_OP | (SUBOP_LSL << OP_SH_SUBOP)) +#define OP_MATCH_LSR (OP_MATCH_FMT1_OP | (SUBOP_LSR << OP_SH_SUBOP)) +#define OP_MATCH_RSB (OP_MATCH_FMT1_OP | (SUBOP_RSB << OP_SH_SUBOP)) +#define OP_MATCH_RSC (OP_MATCH_FMT1_OP | (SUBOP_RSC << OP_SH_SUBOP)) +#define OP_MATCH_AND (OP_MATCH_FMT1_OP | (SUBOP_AND << OP_SH_SUBOP)) +#define OP_MATCH_OR (OP_MATCH_FMT1_OP | (SUBOP_OR << OP_SH_SUBOP)) +#define OP_MATCH_XOR (OP_MATCH_FMT1_OP | (SUBOP_XOR << OP_SH_SUBOP)) +#define OP_MATCH_NOT (OP_MATCH_FMT1_OP | (SUBOP_NOT << OP_SH_SUBOP)) +#define OP_MATCH_MIN (OP_MATCH_FMT1_OP | (SUBOP_MIN << OP_SH_SUBOP)) +#define OP_MATCH_MAX (OP_MATCH_FMT1_OP | (SUBOP_MAX << OP_SH_SUBOP)) +#define OP_MATCH_CLR (OP_MATCH_FMT1_OP | (SUBOP_CLR << OP_SH_SUBOP)) +#define OP_MATCH_SET (OP_MATCH_FMT1_OP | (SUBOP_SET << OP_SH_SUBOP)) + +#define OP_MATCH_JMP (OP_MATCH_FMT2_OP | (SUBOP_JMP << OP_SH_SUBOP)) +#define OP_MATCH_JAL (OP_MATCH_FMT2_OP | (SUBOP_JAL << OP_SH_SUBOP)) +#define OP_MATCH_LDI (OP_MATCH_FMT2_OP | (SUBOP_LDI << OP_SH_SUBOP)) +#define OP_MATCH_LMBD (OP_MATCH_FMT2_OP | (SUBOP_LMBD << OP_SH_SUBOP)) +#define OP_MATCH_SCAN (OP_MATCH_FMT2_OP | (SUBOP_SCAN << OP_SH_SUBOP)) +#define OP_MATCH_HALT (OP_MATCH_FMT2_OP | (SUBOP_HALT << OP_SH_SUBOP)) +#define OP_MATCH_SLP (OP_MATCH_FMT2_OP | (SUBOP_SLP << OP_SH_SUBOP)) +#define OP_MATCH_XFR (OP_MATCH_FMT2_OP | (SUBOP_XFR << OP_SH_SUBOP)) +#define OP_MATCH_SXFR (OP_MATCH_XFR | OP_MASK_XFR_S) +#define OP_MATCH_XIN (OP_MATCH_XFR | (SUBOP_XFR_XIN << OP_SH_SUBOP_XFR)) +#define OP_MATCH_XOUT (OP_MATCH_XFR | (SUBOP_XFR_XOUT << OP_SH_SUBOP_XFR)) +#define OP_MATCH_XCHG (OP_MATCH_XFR | (SUBOP_XFR_XCHG << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXIN (OP_MATCH_SXFR | (SUBOP_XFR_XIN << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXOUT (OP_MATCH_SXFR | (SUBOP_XFR_XOUT << OP_SH_SUBOP_XFR)) +#define OP_MATCH_SXCHG (OP_MATCH_SXFR | (SUBOP_XFR_XCHG << OP_SH_SUBOP_XFR)) +#define OP_MATCH_LOOP (OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP)) +#define OP_MATCH_ILOOP (OP_MATCH_FMT2_OP | (SUBOP_LOOP << OP_SH_SUBOP) \ + | OP_MASK_LOOP_INTERRUPTIBLE) + +#define OP_MATCH_QBGT (OP_MATCH_FMT4_OP | OP_MASK_GT) +#define OP_MATCH_QBGE (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_EQ) +#define OP_MATCH_QBLT (OP_MATCH_FMT4_OP | OP_MASK_LT) +#define OP_MATCH_QBLE (OP_MATCH_FMT4_OP | OP_MASK_LT | OP_MASK_EQ) +#define OP_MATCH_QBEQ (OP_MATCH_FMT4_OP | OP_MASK_EQ) +#define OP_MATCH_QBNE (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_LT) +#define OP_MATCH_QBA (OP_MATCH_FMT4_OP | OP_MASK_GT | OP_MASK_LT \ + | OP_MASK_EQ) + +#define OP_MATCH_QBBS (OP_MATCH_FMT5_OP | OP_MASK_BS) +#define OP_MATCH_QBBC (OP_MATCH_FMT5_OP | OP_MASK_BC) + +#define OP_MATCH_LBBO (OP_MATCH_FMT6AB_OP | OP_MASK_LOADSTORE) +#define OP_MATCH_SBBO (OP_MATCH_FMT6AB_OP) +#define OP_MATCH_LBCO (OP_MATCH_FMT6CD_OP | OP_MASK_LOADSTORE) +#define OP_MATCH_SBCO (OP_MATCH_FMT6CD_OP) + +/* Some special extractions. */ +#define OP_MASK_BROFF (OP_MASK_BROFF98 | OP_MASK_BROFF70) + +#define GET_BROFF_URAW(i) \ + ((GET_INSN_FIELD (BROFF98, i) << 8) | (GET_INSN_FIELD (BROFF70, i) << 0)) + +#define GET_BROFF_SIGNED(i) \ + ((long)(GET_BROFF_URAW (i) - (!!(GET_BROFF_URAW (i) & (1 << 9)) << 10))) + +#define SET_BROFF_URAW(i, v) \ + do { \ + SET_INSN_FIELD (BROFF98, (i), (v) >> 8); \ + SET_INSN_FIELD (BROFF70, (i), (v) & 0xff); \ + } while (0) + +#define GET_BURSTLEN(i) \ + ( (GET_INSN_FIELD (BURSTLEN64, (i)) << 4) | \ + (GET_INSN_FIELD (BURSTLEN31, (i)) << 1) | \ + (GET_INSN_FIELD (BURSTLEN0, (i)) << 0)) + +#define SET_BURSTLEN(i, v) \ + do { \ + SET_INSN_FIELD (BURSTLEN64, (i), (v) >> 4); \ + SET_INSN_FIELD (BURSTLEN31, (i), (v) >> 1); \ + SET_INSN_FIELD (BURSTLEN0, (i), (v) >> 0); \ + } while (0) + +/* Miscellaneous helpers. */ +#define OP_MASK_XFR_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \ + | OP_MASK_SUBOP_XFR | OP_MASK_XFR_S) + +#define OP_MASK_LOOP_OP (OP_MASK_FMT2_OP | OP_MASK_SUBOP \ + | OP_MASK_LOOP_INTERRUPTIBLE) + +/* These are the data structures we use to hold the instruction information. */ +extern const struct pru_opcode pru_opcodes[]; +extern const int bfd_pru_num_opcodes; + +/* These are the data structures used to hold the register information. */ +extern const struct pru_reg pru_regs[]; +extern const int pru_num_regs; + +/* Machine-independent macro for number of opcodes. */ +#define NUMOPCODES bfd_pru_num_opcodes +#define NUMREGISTERS pru_num_regs; + +/* This is made extern so that the assembler can use it to find out + what instruction caused an error. */ +extern const struct pru_opcode *pru_find_opcode (unsigned long); + +#endif /* _PRU_H */ -- cgit v1.1