From 13761a1136a46a4dec5d01e00743e8b267a23c0d Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Thu, 2 May 2013 21:06:15 +0000 Subject: [PATCH] * archures.c: Add some more MSP430 machine numbers. * config.bfd (msp430): Define targ_selvecs. * configure.in: Add bfd_elf32_msp430_ti_vec. * cpu-msp430.c: Add some more MSP430 machine numbers. * elf32-msp430.c Add support for MSP430X relocations. Add support for TI compiler generated relocations. Add support for sym_diff relocations. Add support for relaxing out of range short branches into long branches. Add support for MSP430 attribute section. * reloc.c: Add MSP430X relocations. * targets.c: Add bfd_elf32_msp430_ti_vec. * bfd-in2.h: Regenerate. * configure: Regenerate. * libbfd.h: Regenerate. * readelf.c: Add support for MSP430X architecture. * readelf.exp: Expect -wi test to fail for the MSP430. * config/tc-msp430.c: Add support for the MSP430X architecture. Add code to insert a NOP instruction after any instruction that might change the interrupt state. Add support for the LARGE memory model. Add code to initialise the .MSP430.attributes section. * config/tc-msp430.h: Add support for the MSP430X architecture. * doc/c-msp430.texi: Document the new -mL and -mN command line options. * NEWS: Mention support for the MSP430X architecture. * gas/all/gas.exp: Skip the DIFF1 test for the MSP430. Expect the FORWARD test to pass for the MSP430. Skip the REDEF tests for the MSP430. Expect the 930509A test to fail for the MSP430. * gas/all/sleb128-4.d: Skip for the MSP430. * gas/elf/elf.exp: Set target_machine to msp430 for the MSP430. Skip the EHOPT0 test for the MSP430. Skip the REDEF and EQU-RELOC tests for the MSP430. * gas/elf/section2.e-msp430: New file. * gas/lns/lns-big-delta.d: Remove expectation of 20-bit addresses. * gas/lns/lns.exp: Use alternate LNS COMMON test for the MSP430. * gas/msp430/msp430x.s: New test. * gas/msp430/msp430x.d: Expected disassembly. * gas/msp430/msp430.exp: Run new test. * gas/msp430/opcode.d: Update expected disassembly. * msp430.h: Add MSP430X relocs. Add some more MSP430 machine numbers. Add values used by .MSP430.attributes section. * msp430.h: Add patterns for MSP430X instructions. * Makefile.am: Add emsp430X.c * Makefine.in: Regenerate. * configure.tgt (msp430): Add msp430X emulation. * ldmain.c (multiple_definition): Only disable relaxation if it was enabled by the user. * ldmain.h (RELAXATION_ENABLED_BY_USER): New macro. * emulparams/msp430all.sh: Add support for MSP430X. * emultempl/generic.em: (before_parse): Enable relaxation for the MSP430. * scripttempl/msp430.sc: Reorganize sections. Add .rodata section. * scripttempl/msp430_3.sc: Likewise. * NEWS: Mention support for MSP430X. * ld-elf/flags1.d: Expect this test to pass on the MSP430. * ld-elf/init-fini-arrays.d: Expect this test to fail on the MSP430. * ld-elf/merge.d: Expect this test to pass on the MSP430. * ld-elf/sec64k.exp: Skip these tests for the MSP430. * ld-gc/pr13683.d: Expect this test to fail on the MSP430. * ld-srec/srec.exp: Expect these tests to fail on the MSP430. * ld-undefined/undefined.exp: Expect the UNDEFINED LINE test to fail on the MSP430. * msp430-dis.c: Add support for MSP430X instructions. --- bfd/ChangeLog | 18 + bfd/archures.c | 13 +- bfd/bfd-in2.h | 24 + bfd/config.bfd | 1 + bfd/configure | 1 + bfd/configure.in | 3 +- bfd/cpu-msp430.c | 46 +- bfd/elf32-msp430.c | 1833 +++++++++++++++++++---- bfd/libbfd.h | 15 + bfd/reloc.c | 30 + bfd/targets.c | 6 +- binutils/readelf.c | 209 ++- binutils/testsuite/ChangeLog | 4 + binutils/testsuite/binutils-all/readelf.exp | 6 +- gas/ChangeLog | 12 + gas/NEWS | 2 + gas/config/tc-msp430.c | 2096 +++++++++++++++++++++++---- gas/config/tc-msp430.h | 56 +- gas/doc/c-msp430.texi | 16 +- gas/testsuite/ChangeLog | 19 + gas/testsuite/gas/all/gas.exp | 8 +- gas/testsuite/gas/all/sleb128-4.d | 1 + gas/testsuite/gas/elf/elf.exp | 8 +- gas/testsuite/gas/lns/lns-big-delta.d | 4 +- gas/testsuite/gas/lns/lns.exp | 1 + gas/testsuite/gas/msp430/msp430.exp | 4 +- gas/testsuite/gas/msp430/opcode.d | 8 +- include/elf/ChangeLog | 6 + include/elf/msp430.h | 47 +- include/opcode/ChangeLog | 4 + include/opcode/msp430.h | 70 +- ld/ChangeLog | 16 + ld/Makefile.am | 5 + ld/Makefile.in | 6 + ld/NEWS | 2 + ld/configure.tgt | 2 +- ld/emulparams/msp430all.sh | 9 + ld/emultempl/generic.em | 12 + ld/ldmain.c | 8 +- ld/ldmain.h | 12 +- ld/scripttempl/elf32msp430.sc | 138 +- ld/scripttempl/elf32msp430_3.sc | 16 + ld/testsuite/ChangeLog | 12 + ld/testsuite/ld-elf/flags1.d | 4 +- ld/testsuite/ld-elf/init-fini-arrays.d | 5 +- ld/testsuite/ld-elf/merge.d | 2 +- ld/testsuite/ld-elf/sec64k.exp | 1 + ld/testsuite/ld-gc/pr13683.d | 2 +- ld/testsuite/ld-srec/srec.exp | 8 +- ld/testsuite/ld-undefined/undefined.exp | 5 + opcodes/ChangeLog | 4 + opcodes/msp430-dis.c | 712 +++++++-- 52 files changed, 4740 insertions(+), 812 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 2a0ee47..7fdc531 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,21 @@ +2013-05-02 Nick Clifton + + * archures.c: Add some more MSP430 machine numbers. + * config.bfd (msp430): Define targ_selvecs. + * configure.in: Add bfd_elf32_msp430_ti_vec. + * cpu-msp430.c: Add some more MSP430 machine numbers. + * elf32-msp430.c Add support for MSP430X relocations. + Add support for TI compiler generated relocations. + Add support for sym_diff relocations. + Add support for relaxing out of range short branches into long + branches. + Add support for MSP430 attribute section. + * reloc.c: Add MSP430X relocations. + * targets.c: Add bfd_elf32_msp430_ti_vec. + * bfd-in2.h: Regenerate. + * configure: Regenerate. + * libbfd.h: Regenerate. + 2013-05-01 Maciej W. Rozycki * config.bfd: Replace alpha*-*-linuxecoff* pattern with diff --git a/bfd/archures.c b/bfd/archures.c index 0be72da..f7cef8b 100644 --- a/bfd/archures.c +++ b/bfd/archures.c @@ -1,7 +1,5 @@ /* BFD library support routines for architectures. - Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, - 2012, 2013 Free Software Foundation, Inc. + Copyright 1990-2013 Free Software Foundation, Inc. Hacked by John Gilmore and Steve Chamberlain of Cygnus Support. This file is part of BFD, the Binary File Descriptor library. @@ -437,7 +435,12 @@ DESCRIPTION .#define bfd_mach_msp14 14 .#define bfd_mach_msp15 15 .#define bfd_mach_msp16 16 +.#define bfd_mach_msp20 20 .#define bfd_mach_msp21 21 +.#define bfd_mach_msp22 22 +.#define bfd_mach_msp23 23 +.#define bfd_mach_msp24 24 +.#define bfd_mach_msp26 26 .#define bfd_mach_msp31 31 .#define bfd_mach_msp32 32 .#define bfd_mach_msp33 33 @@ -445,6 +448,10 @@ DESCRIPTION .#define bfd_mach_msp42 42 .#define bfd_mach_msp43 43 .#define bfd_mach_msp44 44 +.#define bfd_mach_msp430x 45 +.#define bfd_mach_msp46 46 +.#define bfd_mach_msp47 47 +.#define bfd_mach_msp54 54 . bfd_arch_xc16x, {* Infineon's XC16X Series. *} .#define bfd_mach_xc16x 1 .#define bfd_mach_xc16xl 2 diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 5c4e9c4..0050f46 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2170,7 +2170,12 @@ enum bfd_architecture #define bfd_mach_msp14 14 #define bfd_mach_msp15 15 #define bfd_mach_msp16 16 +#define bfd_mach_msp20 20 #define bfd_mach_msp21 21 +#define bfd_mach_msp22 22 +#define bfd_mach_msp23 23 +#define bfd_mach_msp24 24 +#define bfd_mach_msp26 26 #define bfd_mach_msp31 31 #define bfd_mach_msp32 32 #define bfd_mach_msp33 33 @@ -2178,6 +2183,10 @@ enum bfd_architecture #define bfd_mach_msp42 42 #define bfd_mach_msp43 43 #define bfd_mach_msp44 44 +#define bfd_mach_msp430x 45 +#define bfd_mach_msp46 46 +#define bfd_mach_msp47 47 +#define bfd_mach_msp54 54 bfd_arch_xc16x, /* Infineon's XC16X Series. */ #define bfd_mach_xc16x 1 #define bfd_mach_xc16xl 2 @@ -4918,6 +4927,21 @@ a matching LO8XG part. */ BFD_RELOC_MSP430_16_BYTE, BFD_RELOC_MSP430_2X_PCREL, BFD_RELOC_MSP430_RL_PCREL, + BFD_RELOC_MSP430_ABS8, + BFD_RELOC_MSP430X_PCR20_EXT_SRC, + BFD_RELOC_MSP430X_PCR20_EXT_DST, + BFD_RELOC_MSP430X_PCR20_EXT_ODST, + BFD_RELOC_MSP430X_ABS20_EXT_SRC, + BFD_RELOC_MSP430X_ABS20_EXT_DST, + BFD_RELOC_MSP430X_ABS20_EXT_ODST, + BFD_RELOC_MSP430X_ABS20_ADR_SRC, + BFD_RELOC_MSP430X_ABS20_ADR_DST, + BFD_RELOC_MSP430X_PCR16, + BFD_RELOC_MSP430X_PCR20_CALL, + BFD_RELOC_MSP430X_ABS16, + BFD_RELOC_MSP430_ABS_HI16, + BFD_RELOC_MSP430_PREL31, + BFD_RELOC_MSP430_SYM_DIFF, /* Relocations used by the Altera Nios II core. */ BFD_RELOC_NIOS2_S16, diff --git a/bfd/config.bfd b/bfd/config.bfd index 5634154..ebc9d40 100644 --- a/bfd/config.bfd +++ b/bfd/config.bfd @@ -1134,6 +1134,7 @@ case "${targ}" in msp430-*-*) targ_defvec=bfd_elf32_msp430_vec + targ_selvecs=bfd_elf32_msp430_ti_vec ;; ns32k-pc532-mach* | ns32k-pc532-ux*) diff --git a/bfd/configure b/bfd/configure index 24f6fd3..5fc08e4 100755 --- a/bfd/configure +++ b/bfd/configure @@ -15292,6 +15292,7 @@ do bfd_elf32_mn10300_vec) tb="$tb elf-m10300.lo elf32.lo $elf" ;; bfd_elf32_mt_vec) tb="$tb elf32-mt.lo elf32.lo $elf" ;; bfd_elf32_msp430_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; + bfd_elf32_msp430_ti_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; bfd_elf32_nbigmips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_nlittlemips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_ntradbigmips_vec | bfd_elf32_ntradbigmips_freebsd_vec) diff --git a/bfd/configure.in b/bfd/configure.in index d9bef83..befcf27 100644 --- a/bfd/configure.in +++ b/bfd/configure.in @@ -1,6 +1,6 @@ dnl Process this file with autoconf to produce a configure script. dnl -dnl Copyright 2012 Free Software Foundation +dnl Copyright 2012-2013 Free Software Foundation dnl dnl This file is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -789,6 +789,7 @@ do bfd_elf32_mn10300_vec) tb="$tb elf-m10300.lo elf32.lo $elf" ;; bfd_elf32_mt_vec) tb="$tb elf32-mt.lo elf32.lo $elf" ;; bfd_elf32_msp430_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; + bfd_elf32_msp430_ti_vec) tb="$tb elf32-msp430.lo elf32.lo $elf" ;; bfd_elf32_nbigmips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_nlittlemips_vec) tb="$tb elfn32-mips.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf32_ntradbigmips_vec | bfd_elf32_ntradbigmips_freebsd_vec) diff --git a/bfd/cpu-msp430.c b/bfd/cpu-msp430.c index 09c0e54..73b6e66 100644 --- a/bfd/cpu-msp430.c +++ b/bfd/cpu-msp430.c @@ -1,6 +1,5 @@ /* BFD library support routines for the MSP architecture. - Copyright (C) 2002, 2003, 2005, 2007, 2012 - Free Software Foundation, Inc. + Copyright (C) 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of BFD, the Binary File Descriptor library. @@ -82,29 +81,56 @@ static const bfd_arch_info_type arch_info_struct[] = /* msp430x16x. */ N (16, bfd_mach_msp16, "msp:16", FALSE, & arch_info_struct[7]), + /* msp430x20x. */ + N (16, bfd_mach_msp20, "msp:20", FALSE, & arch_info_struct[8]), + /* msp430x21x. */ - N (16, bfd_mach_msp21, "msp:21", FALSE, & arch_info_struct[8]), + N (16, bfd_mach_msp21, "msp:21", FALSE, & arch_info_struct[9]), + + /* msp430x22x. */ + N (16, bfd_mach_msp22, "msp:22", FALSE, & arch_info_struct[10]), + + /* msp430x23x. */ + N (16, bfd_mach_msp23, "msp:23", FALSE, & arch_info_struct[11]), + + /* msp430x24x. */ + N (16, bfd_mach_msp24, "msp:24", FALSE, & arch_info_struct[12]), + + /* msp430x26x. */ + N (16, bfd_mach_msp26, "msp:26", FALSE, & arch_info_struct[13]), /* msp430x31x. */ - N (16, bfd_mach_msp31, "msp:31", FALSE, & arch_info_struct[9]), + N (16, bfd_mach_msp31, "msp:31", FALSE, & arch_info_struct[14]), /* msp430x32x. */ - N (16, bfd_mach_msp32, "msp:32", FALSE, & arch_info_struct[10]), + N (16, bfd_mach_msp32, "msp:32", FALSE, & arch_info_struct[15]), /* msp430x33x. */ - N (16, bfd_mach_msp33, "msp:33", FALSE, & arch_info_struct[11]), + N (16, bfd_mach_msp33, "msp:33", FALSE, & arch_info_struct[16]), /* msp430x41x. */ - N (16, bfd_mach_msp41, "msp:41", FALSE, & arch_info_struct[12]), + N (16, bfd_mach_msp41, "msp:41", FALSE, & arch_info_struct[17]), /* msp430x42x. */ - N (16, bfd_mach_msp42, "msp:42", FALSE, & arch_info_struct[13]), + N (16, bfd_mach_msp42, "msp:42", FALSE, & arch_info_struct[18]), /* msp430x43x. */ - N (16, bfd_mach_msp43, "msp:43", FALSE, & arch_info_struct[14]), + N (16, bfd_mach_msp43, "msp:43", FALSE, & arch_info_struct[19]), /* msp430x44x. */ - N (16, bfd_mach_msp43, "msp:44", FALSE, NULL) + N (16, bfd_mach_msp43, "msp:44", FALSE, & arch_info_struct[20]), + + /* msp430x46x. */ + N (16, bfd_mach_msp46, "msp:46", FALSE, & arch_info_struct[21]), + + /* msp430x47x. */ + N (16, bfd_mach_msp47, "msp:47", FALSE, & arch_info_struct[22]), + + /* msp430x54x. */ + N (16, bfd_mach_msp54, "msp:54", FALSE, & arch_info_struct[23]), + + N (32, bfd_mach_msp430x, "msp:430X", FALSE, NULL) + }; const bfd_arch_info_type bfd_msp430_arch = diff --git a/bfd/elf32-msp430.c b/bfd/elf32-msp430.c index b46e72c..6f66282 100644 --- a/bfd/elf32-msp430.c +++ b/bfd/elf32-msp430.c @@ -1,6 +1,5 @@ /* MSP430-specific support for 32-bit ELF - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2010, 2012 - Free Software Foundation, Inc. + Copyright (C) 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of BFD, the Binary File Descriptor library. @@ -27,9 +26,6 @@ #include "elf-bfd.h" #include "elf/msp430.h" -/* Use RELA instead of REL. */ -#undef USE_REL - static reloc_howto_type elf_msp430_howto_table[] = { HOWTO (R_MSP430_NONE, /* type */ @@ -69,7 +65,7 @@ static reloc_howto_type elf_msp430_howto_table[] = 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ - "R_MSP430_13_PCREL", /* name */ + "R_MSP430_10_PCREL", /* name */ FALSE, /* partial_inplace */ 0xfff, /* src_mask */ 0xfff, /* dst_mask */ @@ -90,7 +86,7 @@ static reloc_howto_type elf_msp430_howto_table[] = 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ - /* A 16 bit absolute relocation for command address. */ + /* A 16 bit PC relative relocation for command address. */ HOWTO (R_MSP430_16_PCREL, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ @@ -164,6 +160,340 @@ static reloc_howto_type elf_msp430_howto_table[] = 0, /* src_mask */ 0xffff, /* dst_mask */ TRUE) /* pcrel_offset */ + + /* A 8-bit absolute relocation. */ + , HOWTO (R_MSP430_8, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_8", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + /* Together with a following reloc, allows for the difference + between two symbols to be the real addend of the second reloc. */ + HOWTO (R_MSP430_SYM_DIFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + NULL, /* special handler. */ + "R_MSP430_SYM_DIFF", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE) /* pcrel_offset */ +}; + +static reloc_howto_type elf_msp430x_howto_table[] = +{ + HOWTO (R_MSP430_NONE, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_NONE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MSP430_ABS32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_ABS32", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MSP430_ABS16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_ABS16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MSP430_ABS8, /* 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_generic_reloc, /* special_function */ + "R_MSP430_ABS8", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + FALSE), /* pcrel_offset */ + + HOWTO (R_MSP430_PCR16, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_PCR16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_PCR20_EXT_SRC,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_PCR20_EXT_SRC",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_PCR20_EXT_DST,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_PCR20_EXT_DST",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_PCR20_EXT_ODST,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_PCR20_EXT_ODST",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS20_EXT_SRC,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS20_EXT_SRC",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS20_EXT_DST,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS20_EXT_DST",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS20_EXT_ODST,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS20_EXT_ODST",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS20_ADR_SRC,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS20_ADR_SRC",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS20_ADR_DST,/* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS20_ADR_DST",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_PCR16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_PCR16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_PCR20_CALL, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_PCR20_CALL",/* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430X_ABS16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_ABS16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430_ABS_HI16, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_ABS_HI16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + HOWTO (R_MSP430_PREL31, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430_PREL31", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + EMPTY_HOWTO (R_MSP430_EHTYPE), + + /* A 13 bit PC relative relocation. */ + HOWTO (R_MSP430X_10_PCREL, /* type */ + 1, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_10_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0xfff, /* src_mask */ + 0xfff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A 13 bit PC relative relocation for complicated polymorphs. */ + HOWTO (R_MSP430X_2X_PCREL, /* type */ + 1, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 10, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_MSP430X_2X_PCREL", /* name */ + FALSE, /* partial_inplace */ + 0xfff, /* src_mask */ + 0xfff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* Together with a following reloc, allows for the difference + between two symbols to be the real addend of the second reloc. */ + HOWTO (R_MSP430X_SYM_DIFF, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + NULL, /* special handler. */ + "R_MSP430X_SYM_DIFF", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + FALSE) /* pcrel_offset */ }; /* Map BFD reloc types to MSP430 ELF reloc types. */ @@ -175,18 +505,55 @@ struct msp430_reloc_map }; static const struct msp430_reloc_map msp430_reloc_map[] = - { - {BFD_RELOC_NONE, R_MSP430_NONE}, - {BFD_RELOC_32, R_MSP430_32}, - {BFD_RELOC_MSP430_10_PCREL, R_MSP430_10_PCREL}, - {BFD_RELOC_16, R_MSP430_16_BYTE}, - {BFD_RELOC_MSP430_16_PCREL, R_MSP430_16_PCREL}, - {BFD_RELOC_MSP430_16, R_MSP430_16}, - {BFD_RELOC_MSP430_16_PCREL_BYTE, R_MSP430_16_PCREL_BYTE}, - {BFD_RELOC_MSP430_16_BYTE, R_MSP430_16_BYTE}, - {BFD_RELOC_MSP430_2X_PCREL, R_MSP430_2X_PCREL}, - {BFD_RELOC_MSP430_RL_PCREL, R_MSP430_RL_PCREL} - }; +{ + {BFD_RELOC_NONE, R_MSP430_NONE}, + {BFD_RELOC_32, R_MSP430_32}, + {BFD_RELOC_MSP430_10_PCREL, R_MSP430_10_PCREL}, + {BFD_RELOC_16, R_MSP430_16_BYTE}, + {BFD_RELOC_MSP430_16_PCREL, R_MSP430_16_PCREL}, + {BFD_RELOC_MSP430_16, R_MSP430_16}, + {BFD_RELOC_MSP430_16_PCREL_BYTE, R_MSP430_16_PCREL_BYTE}, + {BFD_RELOC_MSP430_16_BYTE, R_MSP430_16_BYTE}, + {BFD_RELOC_MSP430_2X_PCREL, R_MSP430_2X_PCREL}, + {BFD_RELOC_MSP430_RL_PCREL, R_MSP430_RL_PCREL}, + {BFD_RELOC_8, R_MSP430_8}, + {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430_SYM_DIFF} +}; + +static const struct msp430_reloc_map msp430x_reloc_map[] = +{ + {BFD_RELOC_NONE, R_MSP430_NONE}, + {BFD_RELOC_32, R_MSP430_ABS32}, + {BFD_RELOC_16, R_MSP430_ABS16}, + {BFD_RELOC_8, R_MSP430_ABS8}, + {BFD_RELOC_MSP430_ABS8, R_MSP430_ABS8}, + {BFD_RELOC_MSP430X_PCR20_EXT_SRC, R_MSP430X_PCR20_EXT_SRC}, + {BFD_RELOC_MSP430X_PCR20_EXT_DST, R_MSP430X_PCR20_EXT_DST}, + {BFD_RELOC_MSP430X_PCR20_EXT_ODST, R_MSP430X_PCR20_EXT_ODST}, + {BFD_RELOC_MSP430X_ABS20_EXT_SRC, R_MSP430X_ABS20_EXT_SRC}, + {BFD_RELOC_MSP430X_ABS20_EXT_DST, R_MSP430X_ABS20_EXT_DST}, + {BFD_RELOC_MSP430X_ABS20_EXT_ODST, R_MSP430X_ABS20_EXT_ODST}, + {BFD_RELOC_MSP430X_ABS20_ADR_SRC, R_MSP430X_ABS20_ADR_SRC}, + {BFD_RELOC_MSP430X_ABS20_ADR_DST, R_MSP430X_ABS20_ADR_DST}, + {BFD_RELOC_MSP430X_PCR16, R_MSP430X_PCR16}, + {BFD_RELOC_MSP430X_PCR20_CALL, R_MSP430X_PCR20_CALL}, + {BFD_RELOC_MSP430X_ABS16, R_MSP430X_ABS16}, + {BFD_RELOC_MSP430_ABS_HI16, R_MSP430_ABS_HI16}, + {BFD_RELOC_MSP430_PREL31, R_MSP430_PREL31}, + {BFD_RELOC_MSP430_10_PCREL, R_MSP430X_10_PCREL}, + {BFD_RELOC_MSP430_2X_PCREL, R_MSP430X_2X_PCREL}, + {BFD_RELOC_MSP430_RL_PCREL, R_MSP430X_PCR16}, + {BFD_RELOC_MSP430_SYM_DIFF, R_MSP430X_SYM_DIFF} +}; + +static inline bfd_boolean +uses_msp430x_relocs (bfd * abfd) +{ + extern const bfd_target bfd_elf32_msp430_ti_vec; + + return bfd_get_mach (abfd) == bfd_mach_msp430x + || abfd->xvec == & bfd_elf32_msp430_ti_vec; +} static reloc_howto_type * bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, @@ -194,9 +561,18 @@ bfd_elf32_bfd_reloc_type_lookup (bfd * abfd ATTRIBUTE_UNUSED, { unsigned int i; - for (i = 0; i < ARRAY_SIZE (msp430_reloc_map); i++) - if (msp430_reloc_map[i].bfd_reloc_val == code) - return &elf_msp430_howto_table[msp430_reloc_map[i].elf_reloc_val]; + if (uses_msp430x_relocs (abfd)) + { + for (i = ARRAY_SIZE (msp430x_reloc_map); i--;) + if (msp430x_reloc_map[i].bfd_reloc_val == code) + return elf_msp430x_howto_table + msp430x_reloc_map[i].elf_reloc_val; + } + else + { + for (i = 0; i < ARRAY_SIZE (msp430_reloc_map); i++) + if (msp430_reloc_map[i].bfd_reloc_val == code) + return &elf_msp430_howto_table[msp430_reloc_map[i].elf_reloc_val]; + } return NULL; } @@ -207,13 +583,23 @@ bfd_elf32_bfd_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, { unsigned int i; - for (i = 0; - i < (sizeof (elf_msp430_howto_table) - / sizeof (elf_msp430_howto_table[0])); - i++) - if (elf_msp430_howto_table[i].name != NULL - && strcasecmp (elf_msp430_howto_table[i].name, r_name) == 0) - return &elf_msp430_howto_table[i]; + if (uses_msp430x_relocs (abfd)) + { + for (i = ARRAY_SIZE (elf_msp430x_howto_table); i--;) + if (elf_msp430x_howto_table[i].name != NULL + && strcasecmp (elf_msp430x_howto_table[i].name, r_name) == 0) + return elf_msp430x_howto_table + i; + } + else + { + for (i = 0; + i < (sizeof (elf_msp430_howto_table) + / sizeof (elf_msp430_howto_table[0])); + i++) + if (elf_msp430_howto_table[i].name != NULL + && strcasecmp (elf_msp430_howto_table[i].name, r_name) == 0) + return &elf_msp430_howto_table[i]; + } return NULL; } @@ -228,6 +614,14 @@ msp430_info_to_howto_rela (bfd * abfd ATTRIBUTE_UNUSED, unsigned int r_type; r_type = ELF32_R_TYPE (dst->r_info); + + if (uses_msp430x_relocs (abfd)) + { + BFD_ASSERT (r_type < (unsigned int) R_MSP430x_max); + cache_ptr->howto = elf_msp430x_howto_table + r_type; + return; + } + BFD_ASSERT (r_type < (unsigned int) R_MSP430_max); cache_ptr->howto = &elf_msp430_howto_table[r_type]; } @@ -280,16 +674,413 @@ elf32_msp430_check_relocs (bfd * abfd, struct bfd_link_info * info, routines, but a few relocs, we have to do them ourselves. */ static bfd_reloc_status_type -msp430_final_link_relocate (reloc_howto_type * howto, bfd * input_bfd, - asection * input_section, bfd_byte * contents, - Elf_Internal_Rela * rel, bfd_vma relocation) +msp430_final_link_relocate (reloc_howto_type * howto, + bfd * input_bfd, + asection * input_section, + bfd_byte * contents, + Elf_Internal_Rela * rel, + bfd_vma relocation, + struct bfd_link_info * info) { + static asection * sym_diff_section; + static bfd_vma sym_diff_value; + + struct bfd_elf_section_data * esd = elf_section_data (input_section); bfd_reloc_status_type r = bfd_reloc_ok; bfd_vma x; bfd_signed_vma srel; + bfd_boolean is_rel_reloc = FALSE; + + if (uses_msp430x_relocs (input_bfd)) + { + /* See if we have a REL type relocation. */ + is_rel_reloc = (esd->rel.hdr != NULL); + /* Sanity check - only one type of relocation per section. + FIXME: Theoretically it is possible to have both types, + but if that happens how can we distinguish between the two ? */ + BFD_ASSERT (! is_rel_reloc || ! esd->rela.hdr); + /* If we are using a REL relocation then the addend should be empty. */ + BFD_ASSERT (! is_rel_reloc || rel->r_addend == 0); + } - switch (howto->type) + if (sym_diff_section != NULL) { + BFD_ASSERT (sym_diff_section == input_section); + + if (uses_msp430x_relocs (input_bfd)) + switch (howto->type) + { + case R_MSP430_ABS32: + /* If we are computing a 32-bit value for the location lists + and the result is 0 then we add one to the value. A zero + value can result because of linker relaxation deleteing + prologue instructions and using a value of 1 (for the begin + and end offsets in the location list entry) results in a + nul entry which does not prevent the following entries from + being parsed. */ + if (relocation == sym_diff_value + && strcmp (input_section->name, ".debug_loc") == 0) + ++ relocation; + /* Fall through. */ + case R_MSP430_ABS16: + case R_MSP430X_ABS16: + case R_MSP430_ABS8: + BFD_ASSERT (! is_rel_reloc); + relocation -= sym_diff_value; + break; + + default: + return bfd_reloc_dangerous; + } + else + switch (howto->type) + { + case R_MSP430_32: + case R_MSP430_16: + case R_MSP430_16_BYTE: + case R_MSP430_8: + relocation -= sym_diff_value; + break; + + default: + return bfd_reloc_dangerous; + } + + sym_diff_section = NULL; + } + + if (uses_msp430x_relocs (input_bfd)) + switch (howto->type) + { + case R_MSP430X_SYM_DIFF: + /* Cache the input section and value. + The offset is unreliable, since relaxation may + have reduced the following reloc's offset. */ + BFD_ASSERT (! is_rel_reloc); + sym_diff_section = input_section; + sym_diff_value = relocation; + return bfd_reloc_ok; + + case R_MSP430_ABS16: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents); + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, srel & 0xffff, contents); + break; + + case R_MSP430X_10_PCREL: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents) & 0x3ff; + else + srel += rel->r_addend; + srel -= rel->r_offset; + srel -= 2; /* Branch instructions add 2 to the PC... */ + srel -= (input_section->output_section->vma + + input_section->output_offset); + if (srel & 1) + return bfd_reloc_outofrange; + + /* MSP430 addresses commands as words. */ + srel >>= 1; + + /* Check for an overflow. */ + if (srel < -512 || srel > 511) + { + if (info->disable_target_specific_optimizations < 0) + { + static bfd_boolean warned = FALSE; + if (! warned) + { + info->callbacks->warning + (info, + _("Try enabling relaxation to avoid relocation truncations"), + NULL, input_bfd, input_section, relocation); + warned = TRUE; + } + } + return bfd_reloc_overflow; + } + + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfc00) | (srel & 0x3ff); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_PCR20_EXT_ODST: + /* [0,4]+[48,16] = ---F ---- FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = (bfd_get_16 (input_bfd, contents) & 0xf) << 16; + addend |= bfd_get_16 (input_bfd, contents+4); + srel += addend; + + } + else + srel += rel->r_addend; + srel -= rel->r_offset; + srel -= (input_section->output_section->vma + + input_section->output_offset); + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 6); + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | ((srel >> 16) & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_ABS20_EXT_SRC: + /* [7,4]+[32,16] = -78- FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = (bfd_get_16 (input_bfd, contents) & 0x0780) << 9; + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend; + } + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 4); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf87f) | ((srel << 7) & 0x0780); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430_16_PCREL: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents); + else + srel += rel->r_addend; + srel -= rel->r_offset; + /* Only branch instructions add 2 to the PC... */ + srel -= (input_section->output_section->vma + + input_section->output_offset); + if (srel & 1) + return bfd_reloc_outofrange; + bfd_put_16 (input_bfd, srel & 0xffff, contents); + break; + + case R_MSP430X_PCR20_EXT_DST: + /* [0,4]+[32,16] = ---F FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = (bfd_get_16 (input_bfd, contents) & 0xf) << 16; + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend; + } + else + srel += rel->r_addend; + srel -= rel->r_offset; + srel -= (input_section->output_section->vma + + input_section->output_offset); + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 4); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | (srel & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_PCR20_EXT_SRC: + /* [7,4]+32,16] = -78- FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = ((bfd_get_16 (input_bfd, contents) & 0x0780) << 9); + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend;; + } + else + srel += rel->r_addend; + srel -= rel->r_offset; + /* Only branch instructions add 2 to the PC... */ + srel -= (input_section->output_section->vma + + input_section->output_offset); + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 4); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf87f) | ((srel << 7) & 0x0780); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430_ABS8: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_8 (input_bfd, contents); + else + srel += rel->r_addend; + bfd_put_8 (input_bfd, srel & 0xff, contents); + break; + + case R_MSP430X_ABS20_EXT_DST: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents) & 0xf; + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 4); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | (srel & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_ABS20_EXT_ODST: + /* [0,4]+[48,16] = ---F ---- FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = (bfd_get_16 (input_bfd, contents) & 0xf) << 16; + addend |= bfd_get_16 (input_bfd, contents+4); + srel += addend; + } + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 6); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | (srel & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_ABS20_ADR_SRC: + /* [8,4]+[32,16] = -F-- FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + + addend = ((bfd_get_16 (input_bfd, contents) & 0xf00) << 8); + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend; + } + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 2); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xf0ff) | ((srel << 8) & 0x0f00); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_ABS20_ADR_DST: + /* [0,4]+[32,16] = ---F FFFF */ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = ((bfd_get_16 (input_bfd, contents) & 0xf) << 16); + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend; + } + else + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel & 0xffff), contents + 2); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | (srel & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_ABS16: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents); + else + srel += rel->r_addend; + x = srel; + if (x > 0xffff) + return bfd_reloc_overflow; + bfd_put_16 (input_bfd, srel & 0xffff, contents); + break; + + case R_MSP430_ABS_HI16: + /* The EABI specifies that this must be a RELA reloc. */ + BFD_ASSERT (! is_rel_reloc); + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + srel += rel->r_addend; + bfd_put_16 (input_bfd, (srel >> 16) & 0xffff, contents); + break; + + case R_MSP430X_PCR20_CALL: + /* [0,4]+[32,16] = ---F FFFF*/ + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + { + bfd_vma addend; + addend = (bfd_get_16 (input_bfd, contents) & 0xf) << 16; + addend |= bfd_get_16 (input_bfd, contents+2); + srel += addend; + } + else + srel += rel->r_addend; + srel -= rel->r_offset; + srel -= (input_section->output_section->vma + + input_section->output_offset); + bfd_put_16 (input_bfd, srel & 0xffff, contents + 2); + srel >>= 16; + x = bfd_get_16 (input_bfd, contents); + x = (x & 0xfff0) | (srel & 0xf); + bfd_put_16 (input_bfd, x, contents); + break; + + case R_MSP430X_PCR16: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += bfd_get_16 (input_bfd, contents); + else + srel += rel->r_addend; + srel -= rel->r_offset; + srel -= (input_section->output_section->vma + + input_section->output_offset); + bfd_put_16 (input_bfd, srel & 0xffff, contents); + break; + + case R_MSP430_PREL31: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + if (is_rel_reloc) + srel += (bfd_get_32 (input_bfd, contents) & 0x7fffffff); + else + srel += rel->r_addend; + srel += rel->r_addend; + x = bfd_get_32 (input_bfd, contents); + x = (x & 0x80000000) | ((srel >> 31) & 0x7fffffff); + bfd_put_32 (input_bfd, x, contents); + break; + + default: + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + } + else + switch (howto->type) + { case R_MSP430_10_PCREL: contents += rel->r_offset; srel = (bfd_signed_vma) relocation; @@ -307,8 +1098,22 @@ msp430_final_link_relocate (reloc_howto_type * howto, bfd * input_bfd, /* Check for an overflow. */ if (srel < -512 || srel > 511) - return bfd_reloc_overflow; - + { + if (info->disable_target_specific_optimizations < 0) + { + static bfd_boolean warned = FALSE; + if (! warned) + { + info->callbacks->warning + (info, + _("Try enabling relaxation to avoid relocation truncations"), + NULL, input_bfd, input_section, relocation); + warned = TRUE; + } + } + return bfd_reloc_overflow; + } + x = bfd_get_16 (input_bfd, contents); x = (x & 0xfc00) | (srel & 0x3ff); bfd_put_16 (input_bfd, x, contents); @@ -389,11 +1194,27 @@ msp430_final_link_relocate (reloc_howto_type * howto, bfd * input_bfd, bfd_put_16 (input_bfd, srel & 0xffff, contents); break; - default: - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, rel->r_offset, - relocation, rel->r_addend); - } + case R_MSP430_8: + contents += rel->r_offset; + srel = (bfd_signed_vma) relocation; + srel += rel->r_addend; + + bfd_put_8 (input_bfd, srel & 0xff, contents); + break; + + case R_MSP430_SYM_DIFF: + /* Cache the input section and value. + The offset is unreliable, since relaxation may + have reduced the following reloc's offset. */ + sym_diff_section = input_section; + sym_diff_value = relocation; + return bfd_reloc_ok; + + default: + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + } return r; } @@ -433,7 +1254,12 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, r_type = ELF32_R_TYPE (rel->r_info); r_symndx = ELF32_R_SYM (rel->r_info); - howto = elf_msp430_howto_table + r_type; + + if (uses_msp430x_relocs (input_bfd)) + howto = elf_msp430x_howto_table + r_type; + else + howto = elf_msp430_howto_table + r_type; + h = NULL; sym = NULL; sec = NULL; @@ -446,7 +1272,7 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, name = bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name); - name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name; + name = (name == NULL || * name == 0) ? bfd_section_name (input_bfd, sec) : name; } else { @@ -456,6 +1282,7 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, r_symndx, symtab_hdr, sym_hashes, h, sec, relocation, unresolved_reloc, warned); + name = h->root.root.string; } if (sec != NULL && discarded_section (sec)) @@ -466,7 +1293,7 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, continue; r = msp430_final_link_relocate (howto, input_bfd, input_section, - contents, rel, relocation); + contents, rel, relocation, info); if (r != bfd_reloc_ok) { @@ -476,7 +1303,7 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, { case bfd_reloc_overflow: r = info->callbacks->reloc_overflow - (info, (h ? &h->root : NULL), name, howto->name, + (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0, input_bfd, input_section, rel->r_offset); break; @@ -487,7 +1314,7 @@ elf32_msp430_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, break; case bfd_reloc_outofrange: - msg = _("internal error: out of range error"); + msg = _("internal error: branch/jump to an odd address detected"); break; case bfd_reloc_notsupported: @@ -529,61 +1356,29 @@ bfd_elf_msp430_final_write_processing (bfd * abfd, switch (bfd_get_mach (abfd)) { default: - case bfd_mach_msp110: - val = E_MSP430_MACH_MSP430x11x1; - break; - - case bfd_mach_msp11: - val = E_MSP430_MACH_MSP430x11; - break; - - case bfd_mach_msp12: - val = E_MSP430_MACH_MSP430x12; - break; - - case bfd_mach_msp13: - val = E_MSP430_MACH_MSP430x13; - break; - - case bfd_mach_msp14: - val = E_MSP430_MACH_MSP430x14; - break; - - case bfd_mach_msp15: - val = E_MSP430_MACH_MSP430x15; - break; - - case bfd_mach_msp16: - val = E_MSP430_MACH_MSP430x16; - break; - - case bfd_mach_msp31: - val = E_MSP430_MACH_MSP430x31; - break; - - case bfd_mach_msp32: - val = E_MSP430_MACH_MSP430x32; - break; - - case bfd_mach_msp33: - val = E_MSP430_MACH_MSP430x33; - break; - - case bfd_mach_msp41: - val = E_MSP430_MACH_MSP430x41; - break; - - case bfd_mach_msp42: - val = E_MSP430_MACH_MSP430x42; - break; - - case bfd_mach_msp43: - val = E_MSP430_MACH_MSP430x43; - break; - - case bfd_mach_msp44: - val = E_MSP430_MACH_MSP430x44; - break; + case bfd_mach_msp110: val = E_MSP430_MACH_MSP430x11x1; break; + case bfd_mach_msp11: val = E_MSP430_MACH_MSP430x11; break; + case bfd_mach_msp12: val = E_MSP430_MACH_MSP430x12; break; + case bfd_mach_msp13: val = E_MSP430_MACH_MSP430x13; break; + case bfd_mach_msp14: val = E_MSP430_MACH_MSP430x14; break; + case bfd_mach_msp15: val = E_MSP430_MACH_MSP430x15; break; + case bfd_mach_msp16: val = E_MSP430_MACH_MSP430x16; break; + case bfd_mach_msp31: val = E_MSP430_MACH_MSP430x31; break; + case bfd_mach_msp32: val = E_MSP430_MACH_MSP430x32; break; + case bfd_mach_msp33: val = E_MSP430_MACH_MSP430x33; break; + case bfd_mach_msp41: val = E_MSP430_MACH_MSP430x41; break; + case bfd_mach_msp42: val = E_MSP430_MACH_MSP430x42; break; + case bfd_mach_msp43: val = E_MSP430_MACH_MSP430x43; break; + case bfd_mach_msp44: val = E_MSP430_MACH_MSP430x44; break; + case bfd_mach_msp20: val = E_MSP430_MACH_MSP430x20; break; + case bfd_mach_msp22: val = E_MSP430_MACH_MSP430x22; break; + case bfd_mach_msp23: val = E_MSP430_MACH_MSP430x23; break; + case bfd_mach_msp24: val = E_MSP430_MACH_MSP430x24; break; + case bfd_mach_msp26: val = E_MSP430_MACH_MSP430x26; break; + case bfd_mach_msp46: val = E_MSP430_MACH_MSP430x46; break; + case bfd_mach_msp47: val = E_MSP430_MACH_MSP430x47; break; + case bfd_mach_msp54: val = E_MSP430_MACH_MSP430x54; break; + case bfd_mach_msp430x: val = E_MSP430_MACH_MSP430X; break; } elf_elfheader (abfd)->e_machine = EM_MSP430; @@ -606,64 +1401,32 @@ elf32_msp430_object_p (bfd * abfd) switch (e_mach) { default: - case E_MSP430_MACH_MSP430x11: - e_set = bfd_mach_msp11; - break; - - case E_MSP430_MACH_MSP430x11x1: - e_set = bfd_mach_msp110; - break; - - case E_MSP430_MACH_MSP430x12: - e_set = bfd_mach_msp12; - break; - - case E_MSP430_MACH_MSP430x13: - e_set = bfd_mach_msp13; - break; - - case E_MSP430_MACH_MSP430x14: - e_set = bfd_mach_msp14; - break; - - case E_MSP430_MACH_MSP430x15: - e_set = bfd_mach_msp15; - break; - - case E_MSP430_MACH_MSP430x16: - e_set = bfd_mach_msp16; - break; - - case E_MSP430_MACH_MSP430x31: - e_set = bfd_mach_msp31; - break; - - case E_MSP430_MACH_MSP430x32: - e_set = bfd_mach_msp32; - break; - - case E_MSP430_MACH_MSP430x33: - e_set = bfd_mach_msp33; - break; - - case E_MSP430_MACH_MSP430x41: - e_set = bfd_mach_msp41; - break; - - case E_MSP430_MACH_MSP430x42: - e_set = bfd_mach_msp42; - break; - - case E_MSP430_MACH_MSP430x43: - e_set = bfd_mach_msp43; - break; - - case E_MSP430_MACH_MSP430x44: - e_set = bfd_mach_msp44; - break; + case E_MSP430_MACH_MSP430x11: e_set = bfd_mach_msp11; break; + case E_MSP430_MACH_MSP430x11x1: e_set = bfd_mach_msp110; break; + case E_MSP430_MACH_MSP430x12: e_set = bfd_mach_msp12; break; + case E_MSP430_MACH_MSP430x13: e_set = bfd_mach_msp13; break; + case E_MSP430_MACH_MSP430x14: e_set = bfd_mach_msp14; break; + case E_MSP430_MACH_MSP430x15: e_set = bfd_mach_msp15; break; + case E_MSP430_MACH_MSP430x16: e_set = bfd_mach_msp16; break; + case E_MSP430_MACH_MSP430x31: e_set = bfd_mach_msp31; break; + case E_MSP430_MACH_MSP430x32: e_set = bfd_mach_msp32; break; + case E_MSP430_MACH_MSP430x33: e_set = bfd_mach_msp33; break; + case E_MSP430_MACH_MSP430x41: e_set = bfd_mach_msp41; break; + case E_MSP430_MACH_MSP430x42: e_set = bfd_mach_msp42; break; + case E_MSP430_MACH_MSP430x43: e_set = bfd_mach_msp43; break; + case E_MSP430_MACH_MSP430x44: e_set = bfd_mach_msp44; break; + case E_MSP430_MACH_MSP430x20: e_set = bfd_mach_msp20; break; + case E_MSP430_MACH_MSP430x22: e_set = bfd_mach_msp22; break; + case E_MSP430_MACH_MSP430x23: e_set = bfd_mach_msp23; break; + case E_MSP430_MACH_MSP430x24: e_set = bfd_mach_msp24; break; + case E_MSP430_MACH_MSP430x26: e_set = bfd_mach_msp26; break; + case E_MSP430_MACH_MSP430x46: e_set = bfd_mach_msp46; break; + case E_MSP430_MACH_MSP430x47: e_set = bfd_mach_msp47; break; + case E_MSP430_MACH_MSP430x54: e_set = bfd_mach_msp54; break; + case E_MSP430_MACH_MSP430X: e_set = bfd_mach_msp430x; break; } } - + return bfd_default_set_arch_mach (abfd, bfd_arch_msp430, e_set); } @@ -674,6 +1437,8 @@ elf32_msp430_object_p (bfd * abfd) - Sibling calls. This will affect only 'jump label' polymorph. Without relaxing this enlarges code by 2 bytes. Sibcalls implemented but do not work in gcc's port by the reason I do not know. + - To convert out of range conditional jump instructions (found inside + a function) into inverted jumps over an unconditional branch instruction. Anyway, if a relaxation required, user should pass -relax option to the linker. @@ -797,11 +1562,13 @@ msp430_elf_symbol_address_p (bfd * abfd, return FALSE; } -/* Adjust all local symbols defined as '.section + 0xXXXX' (.section has sec_shndx) - referenced from current and other sections. */ +/* Adjust all local symbols defined as '.section + 0xXXXX' (.section has + sec_shndx) referenced from current and other sections. */ + static bfd_boolean -msp430_elf_relax_adjust_locals(bfd * abfd, asection * sec, bfd_vma addr, - int count, unsigned int sec_shndx, bfd_vma toaddr) +msp430_elf_relax_adjust_locals (bfd * abfd, asection * sec, bfd_vma addr, + int count, unsigned int sec_shndx, + bfd_vma toaddr) { Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *irel; @@ -809,21 +1576,24 @@ msp430_elf_relax_adjust_locals(bfd * abfd, asection * sec, bfd_vma addr, Elf_Internal_Sym *isym; irel = elf_section_data (sec)->relocs; + if (irel == NULL) + return TRUE; + irelend = irel + sec->reloc_count; symtab_hdr = & elf_tdata (abfd)->symtab_hdr; isym = (Elf_Internal_Sym *) symtab_hdr->contents; - - for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + + for (;irel < irelend; irel++) { int sidx = ELF32_R_SYM(irel->r_info); Elf_Internal_Sym *lsym = isym + sidx; - + /* Adjust symbols referenced by .sec+0xXX */ - if (irel->r_addend > addr && irel->r_addend < toaddr + if (irel->r_addend > addr && irel->r_addend < toaddr && lsym->st_shndx == sec_shndx) irel->r_addend -= count; } - + return TRUE; } @@ -863,7 +1633,7 @@ msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr, /* Adjust all the relocs. */ symtab_hdr = & elf_tdata (abfd)->symtab_hdr; isym = (Elf_Internal_Sym *) symtab_hdr->contents; - for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + for (; irel < irelend; irel++) { /* Get the new reloc address. */ if ((irel->r_offset > addr && irel->r_offset < toaddr)) @@ -871,8 +1641,8 @@ msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr, } for (p = abfd->sections; p != NULL; p = p->next) - msp430_elf_relax_adjust_locals(abfd,p,addr,count,sec_shndx,toaddr); - + msp430_elf_relax_adjust_locals (abfd,p,addr,count,sec_shndx,toaddr); + /* Adjust the local symbols defined in this section. */ symtab_hdr = & elf_tdata (abfd)->symtab_hdr; isym = (Elf_Internal_Sym *) symtab_hdr->contents; @@ -901,6 +1671,82 @@ msp430_elf_relax_delete_bytes (bfd * abfd, asection * sec, bfd_vma addr, return TRUE; } +/* Insert two words into a section whilst relaxing. */ + +static bfd_byte * +msp430_elf_relax_add_two_words (bfd * abfd, asection * sec, bfd_vma addr, + int word1, int word2) +{ + 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; + struct elf_link_hash_entry **sym_hashes; + struct elf_link_hash_entry **end_hashes; + unsigned int symcount; + bfd_vma sec_end; + asection *p; + + contents = elf_section_data (sec)->this_hdr.contents; + sec_end = sec->size; + + /* Make space for the new words. */ + contents = bfd_realloc (contents, sec_end + 4); + memmove (contents + addr + 4, contents + addr, sec_end - addr); + + /* Insert the new words. */ + bfd_put_16 (abfd, word1, contents + addr); + bfd_put_16 (abfd, word2, contents + addr + 2); + + /* Update the section information. */ + sec->size += 4; + elf_section_data (sec)->this_hdr.contents = contents; + + /* Adjust all the relocs. */ + irel = elf_section_data (sec)->relocs; + irelend = irel + sec->reloc_count; + + for (; irel < irelend; irel++) + if ((irel->r_offset >= addr && irel->r_offset < sec_end)) + irel->r_offset += 4; + + /* Adjust the local symbols defined in this section. */ + sec_shndx = _bfd_elf_section_from_bfd_section (abfd, sec); + for (p = abfd->sections; p != NULL; p = p->next) + msp430_elf_relax_adjust_locals (abfd, p, addr, -4, + sec_shndx, sec_end); + + /* Adjust the global symbols affected by the move. */ + symtab_hdr = & elf_tdata (abfd)->symtab_hdr; + isym = (Elf_Internal_Sym *) symtab_hdr->contents; + for (isymend = isym + symtab_hdr->sh_info; isym < isymend; isym++) + if (isym->st_shndx == sec_shndx + && isym->st_value >= addr && isym->st_value < sec_end) + isym->st_value += 4; + + /* 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 + && sym_hash->root.u.def.value >= addr + && sym_hash->root.u.def.value < sec_end) + sym_hash->root.u.def.value += 4; + } + + return contents; +} + static bfd_boolean msp430_elf_relax_section (bfd * abfd, asection * sec, struct bfd_link_info * link_info, @@ -913,6 +1759,7 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, bfd_byte * contents = NULL; Elf_Internal_Sym * isymbuf = NULL; + /* Assume nothing changes. */ *again = FALSE; @@ -920,8 +1767,8 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, this section does not have relocs, or if this is not a code section. */ if (link_info->relocatable - || (sec->flags & SEC_RELOC) == 0 - || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0) + || (sec->flags & SEC_RELOC) == 0 + || sec->reloc_count == 0 || (sec->flags & SEC_CODE) == 0) return TRUE; symtab_hdr = & elf_tdata (abfd)->symtab_hdr; @@ -934,13 +1781,21 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, /* Walk through them looking for relaxing opportunities. */ irelend = internal_relocs + sec->reloc_count; + + /* Do code size growing relocs first. */ for (irel = internal_relocs; irel < irelend; irel++) { bfd_vma symval; /* If this isn't something that can be relaxed, then ignore this reloc. */ - if (ELF32_R_TYPE (irel->r_info) != (int) R_MSP430_RL_PCREL) + if (uses_msp430x_relocs (abfd) + && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430X_10_PCREL) + ; + else if (! uses_msp430x_relocs (abfd) + && ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_10_PCREL) + ; + else continue; /* Get the section contents if we haven't done so already. */ @@ -1014,108 +1869,287 @@ msp430_elf_relax_section (bfd * abfd, asection * sec, that would be more work, but would require less memory when the linker is run. */ - /* Try to turn a 16bit pc-relative branch into a 10bit pc-relative - branch. */ - /* Paranoia? paranoia... */ - if (ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_RL_PCREL) - { - bfd_vma value = symval; + bfd_signed_vma value = symval; + int opcode; - /* Deal with pc-relative gunk. */ - value -= (sec->output_section->vma + sec->output_offset); - value -= irel->r_offset; - value += irel->r_addend; + /* Compute the value that will be relocated. */ + value += irel->r_addend; + /* Convert to PC relative. */ + value -= (sec->output_section->vma + sec->output_offset); + value -= irel->r_offset; + value -= 2; + /* Scale. */ + value >>= 1; - /* See if the value will fit in 10 bits, note the high value is - 1016 as the target will be two bytes closer if we are - able to relax. */ - if ((long) value < 1016 && (long) value > -1016) - { - int code0 = 0, code1 = 0, code2 = 0; - int i; - struct rcodes_s *rx; + /* If it is in range then no modifications are needed. */ + if (value >= -512 && value <= 511) + continue; - /* Get the opcode. */ - if (irel->r_offset >= 6) - code0 = bfd_get_16 (abfd, contents + irel->r_offset - 6); + /* Get the opcode. */ + opcode = bfd_get_16 (abfd, contents + irel->r_offset); + + /* Compute the new opcode. We are going to convert: + J label + into: + J 1f + BR[A] #label + 1: */ + switch (opcode & 0xfc00) + { + case 0x3800: opcode = 0x3402; break; /* Jl -> Jge +2 */ + case 0x3400: opcode = 0x3802; break; /* Jge -> Jl +2 */ + case 0x2c00: opcode = 0x2802; break; /* Jhs -> Jlo +2 */ + case 0x2800: opcode = 0x2c02; break; /* Jlo -> Jhs +2 */ + case 0x2400: opcode = 0x2002; break; /* Jeq -> Jne +2 */ + case 0x2000: opcode = 0x2402; break; /* jne -> Jeq +2 */ + case 0x3000: /* jn */ + /* There is no direct inverse of the Jn insn. + FIXME: we could do this as: + Jn 1f + br 2f + 1: br label + 2: */ + continue; + default: + /* Not a conditional branch instruction. */ + /* fprintf (stderr, "unrecog: %x\n", opcode); */ + goto error_return; + } - if (irel->r_offset >= 4) - code1 = bfd_get_16 (abfd, contents + irel->r_offset - 4); + /* Note that we've changed the relocs, section contents, etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; - code2 = bfd_get_16 (abfd, contents + irel->r_offset - 2); + /* Install the new opcode. */ + bfd_put_16 (abfd, opcode, contents + irel->r_offset); - if (code2 != 0x4010) - continue; + /* Insert the new branch instruction. */ + if (uses_msp430x_relocs (abfd)) + { + /* Insert an absolute branch (aka MOVA) instruction. */ + contents = msp430_elf_relax_add_two_words + (abfd, sec, irel->r_offset + 2, 0x0080, 0x0000); + + /* Update the relocation to point to the inserted branch + instruction. Note - we are changing a PC-relative reloc + into an absolute reloc, but this is OK because we have + arranged with the assembler to have the reloc's value be + a (local) symbol, not a section+offset value. */ + irel->r_offset += 2; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430X_ABS20_ADR_SRC); + } + else + { + contents = msp430_elf_relax_add_two_words + (abfd, sec, irel->r_offset + 2, 0x4030, 0x0000); + + /* See comment above about converting a 10-bit PC-rel + relocation into a 16-bit absolute relocation. */ + irel->r_offset += 4; + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430_16); + } - /* Check r4 and r3. */ - for (i = NUMB_RELAX_CODES - 1; i >= 0; i--) - { - rx = &rcode[i]; - if (rx->cdx == 2 && rx->f0 == code0 && rx->f1 == code1) - break; - else if (rx->cdx == 1 && rx->f1 == code1) - break; - else if (rx->cdx == 0) /* This is an unconditional jump. */ - break; - } + /* Growing the section may mean that other + conditional branches need to be fixed. */ + *again = TRUE; + } - /* Check labels: + if (! uses_msp430x_relocs (abfd)) + /* Now perform the relocations that shrink the code size. + We only do this for non msp430x as gas only generates the RL + reloc for the msp430. */ + 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. */ + + /* Try to turn a 16bit pc-relative branch into a 10bit pc-relative + branch. */ + /* Paranoia? paranoia... */ + if (ELF32_R_TYPE (irel->r_info) == (int) R_MSP430_RL_PCREL) + { + bfd_vma value = symval; + + /* Deal with pc-relative gunk. */ + value -= (sec->output_section->vma + sec->output_offset); + value -= irel->r_offset; + value += irel->r_addend; + + /* See if the value will fit in 10 bits, note the high value is + 1016 as the target will be two bytes closer if we are + able to relax. */ + if ((long) value < 1016 && (long) value > -1016) + { + int code0 = 0, code1 = 0, code2 = 0; + int i; + struct rcodes_s *rx; + + /* Get the opcode. */ + if (irel->r_offset >= 6) + code0 = bfd_get_16 (abfd, contents + irel->r_offset - 6); + + if (irel->r_offset >= 4) + code1 = bfd_get_16 (abfd, contents + irel->r_offset - 4); + + code2 = bfd_get_16 (abfd, contents + irel->r_offset - 2); + + if (code2 != 0x4010) + continue; + + /* Check r4 and r3. */ + for (i = NUMB_RELAX_CODES - 1; i >= 0; i--) + { + rx = &rcode[i]; + if (rx->cdx == 2 && rx->f0 == code0 && rx->f1 == code1) + break; + else if (rx->cdx == 1 && rx->f1 == code1) + break; + else if (rx->cdx == 0) /* This is an unconditional jump. */ + break; + } + + /* Check labels: .Label0: ; we do not care about this label - jeq +6 + jeq +6 .Label1: ; make sure there is no label here - jl +4 + jl +4 .Label2: ; make sure there is no label here - br .Label_dst - - So, if there is .Label1 or .Label2 we cannot relax this code. - This actually should not happen, cause for relaxable - instructions we use RL_PCREL reloc instead of 16_PCREL. - Will change this in the future. */ - - if (rx->cdx > 0 - && msp430_elf_symbol_address_p (abfd, sec, isymbuf, - irel->r_offset - 2)) - continue; - if (rx->cdx > 1 - && msp430_elf_symbol_address_p (abfd, sec, isymbuf, - irel->r_offset - 4)) - continue; - - /* Note that we've changed the relocs, section contents, etc. */ - elf_section_data (sec)->relocs = internal_relocs; - elf_section_data (sec)->this_hdr.contents = contents; - symtab_hdr->contents = (unsigned char *) isymbuf; - - /* Fix the relocation's type. */ - if (rx->labels == 3) /* Handle special cases. */ - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), - R_MSP430_2X_PCREL); - else - irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), - R_MSP430_10_PCREL); - - /* Fix the opcode right way. */ - bfd_put_16 (abfd, rx->t0, contents + irel->r_offset - rx->off); - if (rx->t1) - bfd_put_16 (abfd, rx->t1, - contents + irel->r_offset - rx->off + 2); - - /* Delete bytes. */ - if (!msp430_elf_relax_delete_bytes (abfd, sec, - irel->r_offset - rx->off + - rx->ncl, rx->bs)) - goto error_return; - - /* Handle unconditional jumps. */ - if (rx->cdx == 0) - irel->r_offset -= 2; - - /* That will change things, so, we should relax again. - Note that this is not required, and it may be slow. */ - *again = TRUE; - } - } - } + br .Label_dst + + So, if there is .Label1 or .Label2 we cannot relax this code. + This actually should not happen, cause for relaxable + instructions we use RL_PCREL reloc instead of 16_PCREL. + Will change this in the future. */ + + if (rx->cdx > 0 + && msp430_elf_symbol_address_p (abfd, sec, isymbuf, + irel->r_offset - 2)) + continue; + if (rx->cdx > 1 + && msp430_elf_symbol_address_p (abfd, sec, isymbuf, + irel->r_offset - 4)) + continue; + + /* Note that we've changed the relocs, section contents, etc. */ + elf_section_data (sec)->relocs = internal_relocs; + elf_section_data (sec)->this_hdr.contents = contents; + symtab_hdr->contents = (unsigned char *) isymbuf; + + /* Fix the relocation's type. */ + if (uses_msp430x_relocs (abfd)) + { + if (rx->labels == 3) /* Handle special cases. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430X_2X_PCREL); + else + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430X_10_PCREL); + } + else + { + if (rx->labels == 3) /* Handle special cases. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430_2X_PCREL); + else + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_MSP430_10_PCREL); + } + + /* Fix the opcode right way. */ + bfd_put_16 (abfd, rx->t0, contents + irel->r_offset - rx->off); + if (rx->t1) + bfd_put_16 (abfd, rx->t1, + contents + irel->r_offset - rx->off + 2); + + /* Delete bytes. */ + if (!msp430_elf_relax_delete_bytes (abfd, sec, + irel->r_offset - rx->off + + rx->ncl, rx->bs)) + goto error_return; + + /* Handle unconditional jumps. */ + if (rx->cdx == 0) + irel->r_offset -= 2; + + /* 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) { @@ -1159,11 +2193,259 @@ error_return: return FALSE; } +/* Handle an MSP430 specific section when reading an object file. + This is called when bfd_section_from_shdr finds a section with + an unknown type. */ + +static bfd_boolean +elf32_msp430_section_from_shdr (bfd *abfd, + Elf_Internal_Shdr * hdr, + const char *name, + int shindex) +{ + switch (hdr->sh_type) + { + case SHT_MSP430_SEC_FLAGS: + case SHT_MSP430_SYM_ALIASES: + case SHT_MSP430_ATTRIBUTES: + return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex); + default: + return FALSE; + } +} + +static bfd_boolean +elf32_msp430_obj_attrs_handle_unknown (bfd *abfd, int tag) +{ + _bfd_error_handler + (_("Warning: %B: Unknown MSPABI object attribute %d"), + abfd, tag); + return TRUE; +} + +/* Determine whether an object attribute tag takes an integer, a + string or both. */ + +static int +elf32_msp430_obj_attrs_arg_type (int tag) +{ + if (tag == Tag_compatibility) + return ATTR_TYPE_FLAG_INT_VAL | ATTR_TYPE_FLAG_STR_VAL; + + if (tag < 32) + return ATTR_TYPE_FLAG_INT_VAL; + + return (tag & 1) != 0 ? ATTR_TYPE_FLAG_STR_VAL : ATTR_TYPE_FLAG_INT_VAL; +} + +static inline const char * +isa_type (int isa) +{ + switch (isa) + { + case 1: return "MSP430"; + case 2: return "MSP430X"; + default: return "unknown"; + } +} + +static inline const char * +code_model (int model) +{ + switch (model) + { + case 1: return "small"; + case 2: return "large"; + default: return "unknown"; + } +} + +static inline const char * +data_model (int model) +{ + switch (model) + { + case 1: return "small"; + case 2: return "large"; + case 3: return "restricted large"; + default: return "unknown"; + } +} + +/* Merge MSPABI object attributes from IBFD into OBFD. + Raise an error if there are conflicting attributes. */ + +static bfd_boolean +elf32_msp430_merge_mspabi_attributes (bfd *ibfd, bfd *obfd) +{ + obj_attribute *in_attr; + obj_attribute *out_attr; + bfd_boolean result = TRUE; + static bfd * first_input_bfd = NULL; + + /* Skip linker created files. */ + if (ibfd->flags & BFD_LINKER_CREATED) + return TRUE; + + /* If this is the first real object just copy the attributes. */ + if (!elf_known_obj_attributes_proc (obfd)[0].i) + { + _bfd_elf_copy_obj_attributes (ibfd, obfd); + + out_attr = elf_known_obj_attributes_proc (obfd); + + /* Use the Tag_null value to indicate that + the attributes have been initialized. */ + out_attr[0].i = 1; + + first_input_bfd = ibfd; + return TRUE; + } + + in_attr = elf_known_obj_attributes_proc (ibfd); + out_attr = elf_known_obj_attributes_proc (obfd); + + /* The ISAs must be the same. */ + if (in_attr[OFBA_MSPABI_Tag_ISA].i != out_attr[OFBA_MSPABI_Tag_ISA].i) + { + _bfd_error_handler + (_("error: %B uses %s instructions but %B uses %s"), + ibfd, first_input_bfd, + isa_type (in_attr[OFBA_MSPABI_Tag_ISA].i), + isa_type (out_attr[OFBA_MSPABI_Tag_ISA].i)); + result = FALSE; + } + + /* The code models must be the same. */ + if (in_attr[OFBA_MSPABI_Tag_Code_Model].i != + out_attr[OFBA_MSPABI_Tag_Code_Model].i) + { + _bfd_error_handler + (_("error: %B uses the %s code model whereas %B uses the %s code model"), + ibfd, first_input_bfd, + code_model (in_attr[OFBA_MSPABI_Tag_Code_Model].i), + code_model (out_attr[OFBA_MSPABI_Tag_Code_Model].i)); + result = FALSE; + } + + /* The large code model is only supported by the MSP430X. */ + if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 2 + && out_attr[OFBA_MSPABI_Tag_ISA].i != 2) + { + _bfd_error_handler + (_("error: %B uses the large code model but %B uses MSP430 instructions"), + ibfd, first_input_bfd); + result = FALSE; + } + + /* The data models must be the same. */ + if (in_attr[OFBA_MSPABI_Tag_Data_Model].i != + out_attr[OFBA_MSPABI_Tag_Data_Model].i) + { + _bfd_error_handler + (_("error: %B uses the %s data model whereas %B uses the %s data model"), + ibfd, first_input_bfd, + data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i), + data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i)); + result = FALSE; + } + + /* The small code model requires the use of the small data model. */ + if (in_attr[OFBA_MSPABI_Tag_Code_Model].i == 1 + && out_attr[OFBA_MSPABI_Tag_Data_Model].i != 1) + { + _bfd_error_handler + (_("error: %B uses the small code model but %B uses the %s data model"), + ibfd, first_input_bfd, + data_model (out_attr[OFBA_MSPABI_Tag_Data_Model].i)); + result = FALSE; + } + + /* The large data models are only supported by the MSP430X. */ + if (in_attr[OFBA_MSPABI_Tag_Data_Model].i > 1 + && out_attr[OFBA_MSPABI_Tag_ISA].i != 2) + { + _bfd_error_handler + (_("error: %B uses the %s data model but %B only uses MSP430 instructions"), + ibfd, first_input_bfd, + data_model (in_attr[OFBA_MSPABI_Tag_Data_Model].i)); + result = FALSE; + } + + return result; +} + +/* Merge backend specific data from an object file to the output + object file when linking. */ + +static bfd_boolean +elf32_msp430_merge_private_bfd_data (bfd * ibfd, bfd * obfd) +{ + /* Make sure that the machine number reflects the most + advanced version of the MSP architecture required. */ +#define max(a,b) ((a) > (b) ? (a) : (b)) + if (bfd_get_mach (ibfd) != bfd_get_mach (obfd)) + bfd_default_set_arch_mach (obfd, bfd_get_arch (obfd), + max (bfd_get_mach (ibfd), bfd_get_mach (obfd))); +#undef max + + return elf32_msp430_merge_mspabi_attributes (ibfd, obfd); +} + +/* Copy backend specific data from one object module to another. */ + +static bfd_boolean +elf32_msp430_copy_private_bfd_data (bfd *ibfd, bfd *obfd) +{ + /* Copy object attributes. */ + _bfd_elf_copy_obj_attributes (ibfd, obfd); + + return TRUE; +} + +static bfd_boolean +msp430_elf_is_target_special_symbol (bfd *abfd, asymbol *sym) +{ + return _bfd_elf_is_local_label_name (abfd, sym->name); +} + +/* This is gross. The MSP430 EABI says that (sec 11.5): + + "An implementation may choose to use Rel or Rela + type relocations for other relocations." + + But it also says that: + + "Certain relocations are identified as Rela only. [snip] + Where Rela is specified, an implementation must honor + this requirement." + + There is one relocation marked as requiring RELA - R_MSP430_ABS_HI16 - but + to keep things simple we choose to use RELA relocations throughout. The + problem is that the TI compiler generates REL relocations, so we have to + be able to accept those as well. */ + +#define elf_backend_may_use_rel_p 1 +#define elf_backend_may_use_rela_p 1 +#define elf_backend_default_use_rela_p 1 + +#undef elf_backend_obj_attrs_vendor +#define elf_backend_obj_attrs_vendor "mspabi" +#undef elf_backend_obj_attrs_section +#define elf_backend_obj_attrs_section ".MSP430.attributes" +#undef elf_backend_obj_attrs_section_type +#define elf_backend_obj_attrs_section_type SHT_MSP430_ATTRIBUTES +#define elf_backend_section_from_shdr elf32_msp430_section_from_shdr +#define elf_backend_obj_attrs_handle_unknown elf32_msp430_obj_attrs_handle_unknown +#undef elf_backend_obj_attrs_arg_type +#define elf_backend_obj_attrs_arg_type elf32_msp430_obj_attrs_arg_type +#define bfd_elf32_bfd_copy_private_bfd_data elf32_msp430_copy_private_bfd_data +#define bfd_elf32_bfd_merge_private_bfd_data elf32_msp430_merge_private_bfd_data #define ELF_ARCH bfd_arch_msp430 #define ELF_MACHINE_CODE EM_MSP430 #define ELF_MACHINE_ALT1 EM_MSP430_OLD -#define ELF_MAXPAGESIZE 1 +#define ELF_MAXPAGESIZE 4 #define ELF_OSABI ELFOSABI_STANDALONE #define TARGET_LITTLE_SYM bfd_elf32_msp430_vec @@ -1178,5 +2460,30 @@ error_return: #define elf_backend_object_p elf32_msp430_object_p #define elf_backend_post_process_headers _bfd_elf_set_osabi #define bfd_elf32_bfd_relax_section msp430_elf_relax_section +#define bfd_elf32_bfd_is_target_special_symbol msp430_elf_is_target_special_symbol + +#undef elf32_bed +#define elf32_bed elf32_msp430_bed + +#include "elf32-target.h" + +/* The TI compiler sets the OSABI field to ELFOSABI_NONE. */ +#undef TARGET_LITTLE_SYM +#define TARGET_LITTLE_SYM bfd_elf32_msp430_ti_vec + +#undef elf32_bed +#define elf32_bed elf32_msp430_ti_bed + +#undef ELF_OSABI +#define ELF_OSABI ELFOSABI_NONE + +static const struct bfd_elf_special_section msp430_ti_elf_special_sections[] = +{ + /* prefix, prefix_length, suffix_len, type, attributes. */ + { STRING_COMMA_LEN (".TI.symbol.alias"), 0, SHT_MSP430_SYM_ALIASES, 0 }, + { STRING_COMMA_LEN (".TI.section.flags"), 0, SHT_MSP430_SEC_FLAGS, 0 }, + { STRING_COMMA_LEN ("_TI_build_attrib"), 0, SHT_MSP430_ATTRIBUTES, 0 }, + { NULL, 0, 0, 0, 0 } +}; #include "elf32-target.h" diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 6a4b572..1c4d36f 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -2376,6 +2376,21 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_MSP430_16_BYTE", "BFD_RELOC_MSP430_2X_PCREL", "BFD_RELOC_MSP430_RL_PCREL", + "BFD_RELOC_MSP430_ABS8", + "BFD_RELOC_MSP430X_PCR20_EXT_SRC", + "BFD_RELOC_MSP430X_PCR20_EXT_DST", + "BFD_RELOC_MSP430X_PCR20_EXT_ODST", + "BFD_RELOC_MSP430X_ABS20_EXT_SRC", + "BFD_RELOC_MSP430X_ABS20_EXT_DST", + "BFD_RELOC_MSP430X_ABS20_EXT_ODST", + "BFD_RELOC_MSP430X_ABS20_ADR_SRC", + "BFD_RELOC_MSP430X_ABS20_ADR_DST", + "BFD_RELOC_MSP430X_PCR16", + "BFD_RELOC_MSP430X_PCR20_CALL", + "BFD_RELOC_MSP430X_ABS16", + "BFD_RELOC_MSP430_ABS_HI16", + "BFD_RELOC_MSP430_PREL31", + "BFD_RELOC_MSP430_SYM_DIFF", "BFD_RELOC_NIOS2_S16", "BFD_RELOC_NIOS2_U16", "BFD_RELOC_NIOS2_CALL26", diff --git a/bfd/reloc.c b/bfd/reloc.c index e93b3b9..57df51b 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -5662,6 +5662,36 @@ ENUMX BFD_RELOC_MSP430_2X_PCREL ENUMX BFD_RELOC_MSP430_RL_PCREL +ENUMX + BFD_RELOC_MSP430_ABS8 +ENUMX + BFD_RELOC_MSP430X_PCR20_EXT_SRC +ENUMX + BFD_RELOC_MSP430X_PCR20_EXT_DST +ENUMX + BFD_RELOC_MSP430X_PCR20_EXT_ODST +ENUMX + BFD_RELOC_MSP430X_ABS20_EXT_SRC +ENUMX + BFD_RELOC_MSP430X_ABS20_EXT_DST +ENUMX + BFD_RELOC_MSP430X_ABS20_EXT_ODST +ENUMX + BFD_RELOC_MSP430X_ABS20_ADR_SRC +ENUMX + BFD_RELOC_MSP430X_ABS20_ADR_DST +ENUMX + BFD_RELOC_MSP430X_PCR16 +ENUMX + BFD_RELOC_MSP430X_PCR20_CALL +ENUMX + BFD_RELOC_MSP430X_ABS16 +ENUMX + BFD_RELOC_MSP430_ABS_HI16 +ENUMX + BFD_RELOC_MSP430_PREL31 +ENUMX + BFD_RELOC_MSP430_SYM_DIFF ENUMDOC msp430 specific relocation codes diff --git a/bfd/targets.c b/bfd/targets.c index c9fbbc2..c6794ee 100644 --- a/bfd/targets.c +++ b/bfd/targets.c @@ -1,7 +1,5 @@ /* Generic target-file-type support for the BFD library. - Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, - 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Free Software Foundation, Inc. + Copyright 1990-2013 Free Software Foundation, Inc. Written by Cygnus Support. This file is part of BFD, the Binary File Descriptor library. @@ -668,6 +666,7 @@ extern const bfd_target bfd_elf32_mn10200_vec; extern const bfd_target bfd_elf32_mn10300_vec; extern const bfd_target bfd_elf32_mt_vec; extern const bfd_target bfd_elf32_msp430_vec; +extern const bfd_target bfd_elf32_msp430_ti_vec; extern const bfd_target bfd_elf32_nbigmips_vec; extern const bfd_target bfd_elf32_nlittlemips_vec; extern const bfd_target bfd_elf32_ntradbigmips_vec; @@ -1047,6 +1046,7 @@ static const bfd_target * const _bfd_target_vector[] = &bfd_elf32_mn10300_vec, &bfd_elf32_mt_vec, &bfd_elf32_msp430_vec, + &bfd_elf32_msp430_ti_vec, #ifdef BFD64 &bfd_elf32_nbigmips_vec, &bfd_elf32_nlittlemips_vec, diff --git a/binutils/readelf.c b/binutils/readelf.c index a71bf94..6b75fac 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -131,6 +131,7 @@ #include "elf/moxie.h" #include "elf/mt.h" #include "elf/msp430.h" +#include "elf/nios2.h" #include "elf/or32.h" #include "elf/pj.h" #include "elf/ppc.h" @@ -153,8 +154,6 @@ #include "elf/xstormy16.h" #include "elf/xtensa.h" -#include "elf/nios2.h" - #include "getopt.h" #include "libiberty.h" #include "safe-ctype.h" @@ -903,6 +902,17 @@ get_reloc_symindex (bfd_vma reloc_info) return is_32bit_elf ? ELF32_R_SYM (reloc_info) : ELF64_R_SYM (reloc_info); } +static inline bfd_boolean +uses_msp430x_relocs (void) +{ + return + elf_header.e_machine == EM_MSP430 /* Paranoia. */ + /* GCC uses osabi == ELFOSBI_STANDALONE. */ + && (((elf_header.e_flags & EF_MSP430_MACH) == E_MSP430_MACH_MSP430X) + /* TI compiler uses ELFOSABI_NONE. */ + || (elf_header.e_ident[EI_OSABI] == ELFOSABI_NONE)); +} + /* Display the contents of the relocation data found at the specified offset. */ @@ -1125,6 +1135,11 @@ dump_relocations (FILE * file, break; case EM_MSP430: + if (uses_msp430x_relocs ()) + { + rtype = elf_msp430x_reloc_type (type); + break; + } case EM_MSP430_OLD: rtype = elf_msp430_reloc_type (type); break; @@ -2781,6 +2796,32 @@ get_machine_flags (unsigned e_flags, unsigned e_machine) if ((e_flags & EF_C6000_REL)) strcat (buf, ", relocatable module"); break; + + case EM_MSP430: + strcat (buf, _(": architecture variant: ")); + switch (e_flags & EF_MSP430_MACH) + { + case E_MSP430_MACH_MSP430x11: strcat (buf, "MSP430x11"); break; + case E_MSP430_MACH_MSP430x11x1 : strcat (buf, "MSP430x11x1 "); break; + case E_MSP430_MACH_MSP430x12: strcat (buf, "MSP430x12"); break; + case E_MSP430_MACH_MSP430x13: strcat (buf, "MSP430x13"); break; + case E_MSP430_MACH_MSP430x14: strcat (buf, "MSP430x14"); break; + case E_MSP430_MACH_MSP430x15: strcat (buf, "MSP430x15"); break; + case E_MSP430_MACH_MSP430x16: strcat (buf, "MSP430x16"); break; + case E_MSP430_MACH_MSP430x31: strcat (buf, "MSP430x31"); break; + case E_MSP430_MACH_MSP430x32: strcat (buf, "MSP430x32"); break; + case E_MSP430_MACH_MSP430x33: strcat (buf, "MSP430x33"); break; + case E_MSP430_MACH_MSP430x41: strcat (buf, "MSP430x41"); break; + case E_MSP430_MACH_MSP430x42: strcat (buf, "MSP430x42"); break; + case E_MSP430_MACH_MSP430x43: strcat (buf, "MSP430x43"); break; + case E_MSP430_MACH_MSP430x44: strcat (buf, "MSP430x44"); break; + case E_MSP430_MACH_MSP430X : strcat (buf, "MSP430X"); break; + default: + strcat (buf, _(": unknown")); break; + } + + if (e_flags & ~ EF_MSP430_MACH) + strcat (buf, _(": unknown extra flag bits also present")); } } @@ -3206,6 +3247,18 @@ get_tic6x_section_type_name (unsigned int sh_type) } static const char * +get_msp430x_section_type_name (unsigned int sh_type) +{ + switch (sh_type) + { + case SHT_MSP430_SEC_FLAGS: return "MSP430_SEC_FLAGS"; + case SHT_MSP430_SYM_ALIASES: return "MSP430_SYM_ALIASES"; + case SHT_MSP430_ATTRIBUTES: return "MSP430_ATTRIBUTES"; + default: return NULL; + } +} + +static const char * get_section_type_name (unsigned int sh_type) { static char buff[32]; @@ -3270,6 +3323,9 @@ get_section_type_name (unsigned int sh_type) case EM_TI_C6000: result = get_tic6x_section_type_name (sh_type); break; + case EM_MSP430: + result = get_msp430x_section_type_name (sh_type); + break; default: result = NULL; break; @@ -8994,8 +9050,13 @@ get_symbol_type (unsigned int type) default: if (type >= STT_LOPROC && type <= STT_HIPROC) { - if (elf_header.e_machine == EM_ARM && type == STT_ARM_TFUNC) - return "THUMB_FUNC"; + if (elf_header.e_machine == EM_ARM) + { + if (type == STT_ARM_TFUNC) + return "THUMB_FUNC"; + if (type == STT_ARM_16BIT) + return "THUMB_LABEL"; + } if (elf_header.e_machine == EM_SPARCV9 && type == STT_REGISTER) return "REGISTER"; @@ -9961,6 +10022,60 @@ target_specific_reloc_handling (Elf_Internal_Rela * reloc, switch (elf_header.e_machine) { + case EM_MSP430: + case EM_MSP430_OLD: + { + static Elf_Internal_Sym * saved_sym = NULL; + + switch (reloc_type) + { + case 10: /* R_MSP430_SYM_DIFF */ + if (uses_msp430x_relocs ()) + break; + case 21: /* R_MSP430X_SYM_DIFF */ + saved_sym = symtab + get_reloc_symindex (reloc->r_info); + return TRUE; + + case 1: /* R_MSP430_32 or R_MSP430_ABS32 */ + case 3: /* R_MSP430_16 or R_MSP430_ABS8 */ + goto handle_sym_diff; + + case 5: /* R_MSP430_16_BYTE */ + case 9: /* R_MSP430_8 */ + if (uses_msp430x_relocs ()) + break; + goto handle_sym_diff; + + case 2: /* R_MSP430_ABS16 */ + case 15: /* R_MSP430X_ABS16 */ + if (! uses_msp430x_relocs ()) + break; + goto handle_sym_diff; + + handle_sym_diff: + if (saved_sym != NULL) + { + bfd_vma value; + + value = reloc->r_addend + + (symtab[get_reloc_symindex (reloc->r_info)].st_value + - saved_sym->st_value); + + byte_put (start + reloc->r_offset, value, reloc_type == 1 ? 4 : 2); + + saved_sym = NULL; + return TRUE; + } + break; + + default: + if (saved_sym != NULL) + error (_("Unhandled MSP430 reloc type found after SYM_DIFF reloc")); + break; + } + break; + } + case EM_MN10300: case EM_CYGNUS_MN10300: { @@ -10100,7 +10215,7 @@ is_32bit_abs_reloc (unsigned int reloc_type) return reloc_type == 1; /* R_MOXIE_32. */ case EM_MSP430_OLD: case EM_MSP430: - return reloc_type == 1; /* R_MSP43_32. */ + return reloc_type == 1; /* R_MSP430_32 or R_MSP320_ABS32. */ case EM_MT: return reloc_type == 2; /* R_MT_32. */ case EM_ALTERA_NIOS2: @@ -10352,6 +10467,8 @@ is_16bit_abs_reloc (unsigned int reloc_type) case EM_M32C: return reloc_type == 1; /* R_M32C_16 */ case EM_MSP430: + if (uses_msp430x_relocs ()) + return reloc_type == 2; /* R_MSP430_ABS16. */ case EM_MSP430_OLD: return reloc_type == 5; /* R_MSP430_16_BYTE. */ case EM_ALTERA_NIOS2: @@ -11905,6 +12022,79 @@ display_raw_attribute (unsigned char * p, unsigned char * end) putchar ('\n'); } +static unsigned char * +display_msp430x_attribute (unsigned char * p, + const unsigned char * const end) +{ + unsigned int len; + int val; + int tag; + + tag = read_uleb128 (p, & len, end); + p += len; + + switch (tag) + { + case OFBA_MSPABI_Tag_ISA: + val = read_uleb128 (p, &len, end); + p += len; + printf (" Tag_ISA: "); + switch (val) + { + case 0: printf (_("None\n")); break; + case 1: printf (_("MSP430\n")); break; + case 2: printf (_("MSP430X\n")); break; + default: printf ("??? (%d)\n", val); break; + } + break; + + case OFBA_MSPABI_Tag_Code_Model: + val = read_uleb128 (p, &len, end); + p += len; + printf (" Tag_Code_Model: "); + switch (val) + { + case 0: printf (_("None\n")); break; + case 1: printf (_("Small\n")); break; + case 2: printf (_("Large\n")); break; + default: printf ("??? (%d)\n", val); break; + } + break; + + case OFBA_MSPABI_Tag_Data_Model: + val = read_uleb128 (p, &len, end); + p += len; + printf (" Tag_Data_Model: "); + switch (val) + { + case 0: printf (_("None\n")); break; + case 1: printf (_("Small\n")); break; + case 2: printf (_("Large\n")); break; + case 3: printf (_("Restricted Large\n")); break; + default: printf ("??? (%d)\n", val); break; + } + break; + + default: + printf (_(" : "), tag); + + if (tag & 1) + { + printf ("\"%s\"\n", p); + p += strlen ((char *) p) + 1; + } + else + { + val = read_uleb128 (p, &len, end); + p += len; + printf ("%d (0x%x)\n", val, val); + } + break; + } + + return p; +} + static int process_attributes (FILE * file, const char * public_name, @@ -12076,6 +12266,13 @@ process_tic6x_specific (FILE * file) display_tic6x_attribute, NULL); } +static int +process_msp430x_specific (FILE * file) +{ + return process_attributes (file, "mspabi", SHT_MSP430_ATTRIBUTES, + display_msp430x_attribute, NULL); +} + /* DATA points to the contents of a MIPS GOT that starts at VMA PLTGOT. Print the Address, Access and Initial fields of an entry at VMA ADDR and return the VMA of the next entry. */ @@ -13554,6 +13751,8 @@ process_arch_specific (FILE * file) case EM_TI_C6000: return process_tic6x_specific (file); break; + case EM_MSP430: + return process_msp430x_specific (file); default: break; } diff --git a/binutils/testsuite/ChangeLog b/binutils/testsuite/ChangeLog index cd12d38..37cb9d6 100644 --- a/binutils/testsuite/ChangeLog +++ b/binutils/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2013-05-02 Nick Clifton + + * readelf.exp: Expect -wi test to fail for the MSP430. + 2013-05-01 Maciej W. Rozycki * lib/binutils-common.exp (is_elf_format): Also exclude diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp index c1d7286..db56a86 100644 --- a/binutils/testsuite/binutils-all/readelf.exp +++ b/binutils/testsuite/binutils-all/readelf.exp @@ -1,5 +1,4 @@ -# Copyright 1999, 2000, 2001, 2003, 2004, 2007, 2009, 2012 -# Free Software Foundation, Inc. +# Copyright 1999-2013 Free Software Foundation, Inc. # 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 @@ -196,6 +195,9 @@ proc readelf_wi_test {} { ".*\(DW_OP_addr: 0\).*" } + # The MSP430 in LARGE mode does not generate a DW_OP_addr. + setup_xfail msp430*-*-* + foreach looked_for $sought { set lines [grep $output $looked_for] if ![llength $lines] then { diff --git a/gas/ChangeLog b/gas/ChangeLog index 1eba1b2..e4f896a 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,15 @@ +2013-05-02 Nick Clifton + + * config/tc-msp430.c: Add support for the MSP430X architecture. + Add code to insert a NOP instruction after any instruction that + might change the interrupt state. + Add support for the LARGE memory model. + Add code to initialise the .MSP430.attributes section. + * config/tc-msp430.h: Add support for the MSP430X architecture. + * doc/c-msp430.texi: Document the new -mL and -mN command line + options. + * NEWS: Mention support for the MSP430X architecture. + 2013-05-01 Maciej W. Rozycki * configure.tgt: Replace alpha*-*-linuxecoff* pattern with diff --git a/gas/NEWS b/gas/NEWS index 202db36..29b0fdf 100644 --- a/gas/NEWS +++ b/gas/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Texas Instruments MSP430X processor. + * Add -gdwarf-sections command line option to enable per-code-section generation of DWARF .debug_line sections. diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c index 8ec7546..0fed29a 100644 --- a/gas/config/tc-msp430.c +++ b/gas/config/tc-msp430.c @@ -1,7 +1,6 @@ /* tc-msp430.c -- Assembler code for the Texas Instruments MSP430 - Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 - Free Software Foundation, Inc. + Copyright (C) 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of GAS, the GNU Assembler. @@ -28,6 +27,7 @@ #include "opcode/msp430.h" #include "safe-ctype.h" #include "dwarf2dbg.h" +#include "elf/msp430.h" /* We will disable polymorphs by default because it is dangerous. The potential problem here is the following: assume we got the @@ -39,7 +39,7 @@ .l1: nop ret - + In case of assembly time relaxation we'll get: 0: jmp .l1 <.text +0x08> (reloc deleted) 2: nop @@ -63,15 +63,18 @@ If polymorphs are enabled, and relax isn't, treat all jumps as long jumps, do not delete any relocs and leave them for linker. - + If relax is enabled, relax at assembly time and kill relocs as necessary. */ int msp430_enable_relax; int msp430_enable_polys; +/* Set linkrelax here to avoid fixups in most sections. */ +int linkrelax = 1; + /* GCC uses the some condition codes which we'll implement as new polymorph instructions. - + COND EXPL SHORT JUMP LONG JUMP =============================================== eq == jeq jne +4; br lab @@ -80,7 +83,7 @@ int msp430_enable_polys; ltn honours no-overflow flag ltn < jn jn +2; jmp +4; br lab - lt < jl jge +4; br lab + lt < jl jge +4; br lab ltu < jlo lhs +4; br lab le <= see below leu <= see below @@ -93,14 +96,14 @@ int msp430_enable_polys; Therefore, new opcodes are (BranchEQ -> beq; and so on...) beq,bne,blt,bltn,bltu,bge,bgeu - 'u' means unsigned compares - + 'u' means unsigned compares + Also, we add 'jump' instruction: jump UNCOND -> jmp br lab They will have fmt == 4, and insn_opnumb == number of instruction. */ -struct rcodes_s +struct rcodes_s { char * name; int index; /* Corresponding insn_opnumb. */ @@ -114,7 +117,7 @@ struct rcodes_s #define MSP430_RLC(n,i,sop,o1) \ {#n, i, sop, 2, (o1 + 2), 0x4010, 0} -static struct rcodes_s msp430_rcodes[] = +static struct rcodes_s msp430_rcodes[] = { MSP430_RLC (beq, 0, 0x2400, 0x2000), MSP430_RLC (bne, 1, 0x2000, 0x2400), @@ -126,11 +129,27 @@ static struct rcodes_s msp430_rcodes[] = {"jump", 7, 0x3c00, 1, 0x4010, 0, 0}, {0,0,0,0,0,0,0} }; -#undef MSP430_RLC +#undef MSP430_RLC +#define MSP430_RLC(n,i,sop,o1) \ + {#n, i, sop, 2, (o1 + 2), 0x0030, 0} + +static struct rcodes_s msp430x_rcodes[] = +{ + MSP430_RLC (beq, 0, 0x2400, 0x2000), + MSP430_RLC (bne, 1, 0x2000, 0x2400), + MSP430_RLC (blt, 2, 0x3800, 0x3400), + MSP430_RLC (bltu, 3, 0x2800, 0x2c00), + MSP430_RLC (bge, 4, 0x3400, 0x3800), + MSP430_RLC (bgeu, 5, 0x2c00, 0x2800), + {"bltn", 6, 0x3000, 3, 0x0030 + 1, 0x3c00 + 2, 0x3000}, + {"jump", 7, 0x3c00, 1, 0x0030, 0, 0}, + {0,0,0,0,0,0,0} +}; +#undef MSP430_RLC /* More difficult than above and they have format 5. - + COND EXPL SHORT LONG ================================================================= gt > jeq +2; jge label jeq +6; jl +4; br label @@ -139,9 +158,9 @@ static struct rcodes_s msp430_rcodes[] = le <= jeq label; jl label jeq +2; jge +4; br label ================================================================= */ -struct hcodes_s +struct hcodes_s { - char * name; + char * name; int index; /* Corresponding insn_opnumb. */ int tlab; /* Number of labels in short mode. */ int op0; /* Opcode for first word of short jump. */ @@ -151,7 +170,7 @@ struct hcodes_s int lop2; }; -static struct hcodes_s msp430_hcodes[] = +static struct hcodes_s msp430_hcodes[] = { {"bgt", 0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x4010 }, {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x4010 }, @@ -160,6 +179,15 @@ static struct hcodes_s msp430_hcodes[] = {0,0,0,0,0,0,0,0} }; +static struct hcodes_s msp430x_hcodes[] = +{ + {"bgt", 0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x0030 }, + {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x0030 }, + {"bleu", 2, 2, 0x2400, 0x2800, 0x2401, 0x2c02, 0x0030 }, + {"ble", 3, 2, 0x2400, 0x3800, 0x2401, 0x3402, 0x0030 }, + {0,0,0,0,0,0,0,0} +}; + const char comment_chars[] = ";"; const char line_comment_chars[] = "#"; const char line_separator_chars[] = "{"; @@ -243,7 +271,12 @@ struct mcu_type_s #define MSP430_ISA_14 14 #define MSP430_ISA_15 15 #define MSP430_ISA_16 16 +#define MSP430_ISA_20 20 #define MSP430_ISA_21 21 +#define MSP430_ISA_22 22 +#define MSP430_ISA_23 23 +#define MSP430_ISA_24 24 +#define MSP430_ISA_26 26 #define MSP430_ISA_31 31 #define MSP430_ISA_32 32 #define MSP430_ISA_33 33 @@ -251,14 +284,43 @@ struct mcu_type_s #define MSP430_ISA_42 42 #define MSP430_ISA_43 43 #define MSP430_ISA_44 44 - -#define CHECK_RELOC_MSP430 ((imm_op || byte_op)?BFD_RELOC_MSP430_16_BYTE:BFD_RELOC_MSP430_16) -#define CHECK_RELOC_MSP430_PCREL ((imm_op || byte_op)?BFD_RELOC_MSP430_16_PCREL_BYTE:BFD_RELOC_MSP430_16_PCREL) +#define MSP430X_ISA 45 +#define MSP430_ISA_46 46 +#define MSP430_ISA_47 47 +#define MSP430_ISA_54 54 + +/* Generate a 16-bit relocation. + For the 430X we generate a relocation without linkwer range checking + if the value is being used in an extended (ie 20-bit) instruction. + For the 430 we generate a relocation without assembler range checking + if we are handling an immediate value or a byte-width instruction. */ +#undef CHECK_RELOC_MSP430 +#define CHECK_RELOC_MSP430 \ + (msp430_mcu->isa == MSP430X_ISA \ + ? (extended_op ? BFD_RELOC_16 : BFD_RELOC_MSP430X_ABS16) \ + : ((imm_op || byte_op) \ + ? BFD_RELOC_MSP430_16_BYTE : BFD_RELOC_MSP430_16)) + +/* Generate a 16-bit pc-relative relocation. + For the 430X we generate a relocation without linkwer range checking. + For the 430 we generate a relocation without assembler range checking + if we are handling an immediate value or a byte-width instruction. */ +#undef CHECK_RELOC_MSP430_PCREL +#define CHECK_RELOC_MSP430_PCREL \ + (msp430_mcu->isa == MSP430X_ISA \ + ? BFD_RELOC_MSP430X_PCR16 \ + : (imm_op || byte_op) \ + ? BFD_RELOC_MSP430_16_PCREL_BYTE : BFD_RELOC_MSP430_16_PCREL) static struct mcu_type_s mcu_types[] = { {"msp1", MSP430_ISA_11, bfd_mach_msp11}, {"msp2", MSP430_ISA_14, bfd_mach_msp14}, + {"msp3", MSP430_ISA_20, bfd_mach_msp20}, + {"msp4", MSP430_ISA_24, bfd_mach_msp24}, + {"msp5", MSP430_ISA_31, bfd_mach_msp31}, + {"msp6", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x110", MSP430_ISA_11, bfd_mach_msp11}, {"msp430x112", MSP430_ISA_11, bfd_mach_msp11}, {"msp430x1101", MSP430_ISA_110, bfd_mach_msp110}, @@ -279,6 +341,9 @@ static struct mcu_type_s mcu_types[] = {"msp430x147", MSP430_ISA_14, bfd_mach_msp14}, {"msp430x148", MSP430_ISA_14, bfd_mach_msp14}, {"msp430x149", MSP430_ISA_14, bfd_mach_msp14}, + {"msp430x1471", MSP430_ISA_14, bfd_mach_msp14}, + {"msp430x1481", MSP430_ISA_14, bfd_mach_msp14}, + {"msp430x1491", MSP430_ISA_14, bfd_mach_msp14}, {"msp430x155", MSP430_ISA_15, bfd_mach_msp15}, {"msp430x156", MSP430_ISA_15, bfd_mach_msp15}, @@ -290,11 +355,50 @@ static struct mcu_type_s mcu_types[] = {"msp430x1611", MSP430_ISA_16, bfd_mach_msp16}, {"msp430x1612", MSP430_ISA_16, bfd_mach_msp16}, + {"msp430x2001", MSP430_ISA_20, bfd_mach_msp20}, + {"msp430x2011", MSP430_ISA_20, bfd_mach_msp20}, + {"msp430x2002", MSP430_ISA_20, bfd_mach_msp20}, + {"msp430x2012", MSP430_ISA_20, bfd_mach_msp20}, + {"msp430x2003", MSP430_ISA_20, bfd_mach_msp20}, + {"msp430x2013", MSP430_ISA_20, bfd_mach_msp20 }, + {"msp430x2101", MSP430_ISA_21, bfd_mach_msp21}, {"msp430x2111", MSP430_ISA_21, bfd_mach_msp21}, + {"msp430x2112", MSP430_ISA_21, bfd_mach_msp21}, {"msp430x2121", MSP430_ISA_21, bfd_mach_msp21}, {"msp430x2131", MSP430_ISA_21, bfd_mach_msp21}, - + {"msp430x2132", MSP430_ISA_21, bfd_mach_msp21}, + + {"msp430x2232", MSP430_ISA_22, bfd_mach_msp22}, + {"msp430x2234", MSP430_ISA_22, bfd_mach_msp22}, + {"msp430x2252", MSP430_ISA_22, bfd_mach_msp22}, + {"msp430x2254", MSP430_ISA_22, bfd_mach_msp22}, + {"msp430x2272", MSP430_ISA_22, bfd_mach_msp22}, + {"msp430x2274", MSP430_ISA_22, bfd_mach_msp22}, + + {"msp430x233", MSP430_ISA_23, bfd_mach_msp23}, + {"msp430x235", MSP430_ISA_23, bfd_mach_msp23}, + {"msp430x2330", MSP430_ISA_23, bfd_mach_msp23}, + {"msp430x2350", MSP430_ISA_23, bfd_mach_msp23}, + {"msp430x2370", MSP430_ISA_23, bfd_mach_msp23}, + + {"msp430x247", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2471", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x248", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2481", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x249", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2491", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2410", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2416", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2417", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2418", MSP430_ISA_24, bfd_mach_msp24}, + {"msp430x2419", MSP430_ISA_24, bfd_mach_msp24}, + + {"msp430x2616", MSP430_ISA_26, bfd_mach_msp26}, + {"msp430x2617", MSP430_ISA_26, bfd_mach_msp26}, + {"msp430x2618", MSP430_ISA_26, bfd_mach_msp26}, + {"msp430x2619", MSP430_ISA_26, bfd_mach_msp26}, + {"msp430x311", MSP430_ISA_31, bfd_mach_msp31}, {"msp430x312", MSP430_ISA_31, bfd_mach_msp31}, {"msp430x313", MSP430_ISA_31, bfd_mach_msp31}, @@ -310,9 +414,23 @@ static struct mcu_type_s mcu_types[] = {"msp430x415", MSP430_ISA_41, bfd_mach_msp41}, {"msp430x417", MSP430_ISA_41, bfd_mach_msp41}, + {"msp430x423", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x425", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x427", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x4250", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x4260", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430x4270", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xG4250",MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xG4260",MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xG4270",MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xE423", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xE4232",MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xE4242",MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xE4252",MSP430_ISA_42, bfd_mach_msp42}, {"msp430xE425", MSP430_ISA_42, bfd_mach_msp42}, {"msp430xE427", MSP430_ISA_42, bfd_mach_msp42}, + {"msp430xE4272",MSP430_ISA_42, bfd_mach_msp42}, {"msp430xW423", MSP430_ISA_42, bfd_mach_msp42}, {"msp430xW425", MSP430_ISA_42, bfd_mach_msp42}, @@ -323,12 +441,43 @@ static struct mcu_type_s mcu_types[] = {"msp430xG439", MSP430_ISA_43, bfd_mach_msp43}, {"msp430x435", MSP430_ISA_43, bfd_mach_msp43}, + {"msp430x4351", MSP430_ISA_43, bfd_mach_msp43}, {"msp430x436", MSP430_ISA_43, bfd_mach_msp43}, + {"msp430x4361", MSP430_ISA_43, bfd_mach_msp43}, {"msp430x437", MSP430_ISA_43, bfd_mach_msp43}, + {"msp430x4371", MSP430_ISA_43, bfd_mach_msp43}, + {"msp430x447", MSP430_ISA_44, bfd_mach_msp44}, {"msp430x448", MSP430_ISA_44, bfd_mach_msp44}, {"msp430x449", MSP430_ISA_44, bfd_mach_msp44}, + {"msp430xG4616",MSP430_ISA_46, bfd_mach_msp46}, + {"msp430xG4617",MSP430_ISA_46, bfd_mach_msp46}, + {"msp430xG4618",MSP430_ISA_46, bfd_mach_msp46}, + {"msp430xG4619",MSP430_ISA_46, bfd_mach_msp46}, + + {"msp430x4783", MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x4784", MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x4793", MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x4794", MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47166",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47176",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47186",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47196",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47167",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47177",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47187",MSP430_ISA_47, bfd_mach_msp47}, + {"msp430x47197",MSP430_ISA_47, bfd_mach_msp47}, + + {"msp430x5418", MSP430_ISA_54, bfd_mach_msp54}, + {"msp430x5419", MSP430_ISA_54, bfd_mach_msp54}, + {"msp430x5435", MSP430_ISA_54, bfd_mach_msp54}, + {"msp430x5436", MSP430_ISA_54, bfd_mach_msp54}, + {"msp430x5437", MSP430_ISA_54, bfd_mach_msp54}, + {"msp430x5438", MSP430_ISA_54, bfd_mach_msp54}, + + {"msp430X", MSP430X_ISA, bfd_mach_msp430x}, + {NULL, 0, 0} }; @@ -699,6 +848,10 @@ extract_word (char * from, char * to, int limit) #define OPTION_MMCU 'm' #define OPTION_RELAX 'Q' #define OPTION_POLYMORPHS 'P' +#define OPTION_LARGE 'l' +static bfd_boolean large_model = FALSE; +#define OPTION_NO_INTR_NOPS 'N' +static bfd_boolean gen_interrupt_nops = TRUE; static void msp430_set_arch (int dummy ATTRIBUTE_UNUSED) @@ -719,7 +872,11 @@ show_mcu_list (FILE * stream) fprintf (stream, _("Known MCU names:\n")); for (i = 0; mcu_types[i].name; i++) - fprintf (stream, _("\t %s\n"), mcu_types[i].name); + { + fprintf (stream, "%13.13s", mcu_types[i].name); + if ((i % 6) == 5) + fprintf (stream, "\n"); + } fprintf (stream, "\n"); } @@ -744,21 +901,29 @@ md_parse_option (int c, char * arg) if (msp430_mcu == &default_mcu || msp430_mcu->mach == mcu_types[i].mach) msp430_mcu = &mcu_types[i]; + else if (msp430_mcu->mach == bfd_mach_msp430x) + /* Allow switching to a lesser architecture. */ + msp430_mcu = mcu_types + i; else as_fatal (_("redefinition of mcu type %s' to %s'"), msp430_mcu->name, mcu_types[i].name); return 1; - break; - + case OPTION_RELAX: - msp430_enable_relax = 1; + msp430_enable_relax = 1; return 1; - break; - + case OPTION_POLYMORPHS: msp430_enable_polys = 1; return 1; - break; + + case OPTION_LARGE: + large_model = TRUE; + return 1; + + case OPTION_NO_INTR_NOPS: + gen_interrupt_nops = FALSE; + return 1; } return 0; @@ -779,6 +944,8 @@ struct option md_longopts[] = {"mmcu", required_argument, NULL, OPTION_MMCU}, {"mP", no_argument, NULL, OPTION_POLYMORPHS}, {"mQ", no_argument, NULL, OPTION_RELAX}, + {"ml", no_argument, NULL, OPTION_LARGE}, + {"mN", no_argument, NULL, OPTION_NO_INTR_NOPS}, {NULL, no_argument, NULL, 0} }; @@ -789,30 +956,14 @@ md_show_usage (FILE * stream) { fprintf (stream, _("MSP430 options:\n" - " -mmcu=[msp430-name] select microcontroller type\n" - " msp430x110 msp430x112\n" - " msp430x1101 msp430x1111\n" - " msp430x1121 msp430x1122 msp430x1132\n" - " msp430x122 msp430x123\n" - " msp430x1222 msp430x1232\n" - " msp430x133 msp430x135\n" - " msp430x1331 msp430x1351\n" - " msp430x147 msp430x148 msp430x149\n" - " msp430x155 msp430x156 msp430x157\n" - " msp430x167 msp430x168 msp430x169\n" - " msp430x1610 msp430x1611 msp430x1612\n" - " msp430x311 msp430x312 msp430x313 msp430x314 msp430x315\n" - " msp430x323 msp430x325\n" - " msp430x336 msp430x337\n" - " msp430x412 msp430x413 msp430x415 msp430x417\n" - " msp430xE423 msp430xE425 msp430E427\n" - " msp430xW423 msp430xW425 msp430W427\n" - " msp430xG437 msp430xG438 msp430G439\n" - " msp430x435 msp430x436 msp430x437\n" - " msp430x447 msp430x448 msp430x449\n")); + " -mmcu= select microcontroller type\n")); fprintf (stream, _(" -mQ - enable relaxation at assembly time. DANGEROUS!\n" " -mP - enable polymorph instructions\n")); + fprintf (stream, + _(" -ml - enable large code model\n")); + fprintf (stream, + _(" -mN - disable generation of NOP after changing interrupts\n")); show_mcu_list (stream); } @@ -820,7 +971,7 @@ md_show_usage (FILE * stream) symbolS * md_undefined_symbol (char * name ATTRIBUTE_UNUSED) { - return 0; + return NULL; } static char * @@ -858,31 +1009,49 @@ md_begin (void) bfd_set_arch_mach (stdoutput, TARGET_ARCH, msp430_mcu->mach); } -static int +/* Returns the register number equivalent to the string T. + Returns -1 if there is no such register. + Skips a leading 'r' or 'R' character if there is one. + Handles the register aliases PC and SP. */ + +static signed int check_reg (char * t) { - /* If this is a reg numb, str 't' must be a number from 0 - 15. */ + signed int val; - if (strlen (t) > 2 && *(t + 2) != '+') - return 1; + if (t == NULL) + return -1; - while (*t) - { - if ((*t < '0' || *t > '9') && *t != '+') - break; - t++; - } + if (*t == 'r' || *t == 'R') + ++t; + + if (strncasecmp (t, "pc", 2) == 0) + return 0; - if (*t) + if (strncasecmp (t, "sp", 2) == 0) return 1; - return 0; -} + if (strncasecmp (t, "sr", 2) == 0) + return 2; + + if (*t == '0') + return 0; + val = atoi (t); + + if (val < 1 || val > 15) + return -1; + + return val; +} static int msp430_srcoperand (struct msp430_operand_s * op, - char * l, int bin, int * imm_op) + char * l, + int bin, + int * imm_op, + bfd_boolean allow_20bit_values, + bfd_boolean constants_allowed) { char *__tl = l; @@ -963,7 +1132,15 @@ msp430_srcoperand (struct msp430_operand_s * op, x = op->exp.X_add_number; } - if (op->exp.X_add_number > 65535 || op->exp.X_add_number < -32768) + if (allow_20bit_values) + { + if (op->exp.X_add_number > 0xfffff || op->exp.X_add_number < - (0x7ffff)) + { + as_bad (_("value 0x%x out of extended range."), x); + return 1; + } + } + else if (op->exp.X_add_number > 65535 || op->exp.X_add_number < -32768) { as_bad (_("value %d out of range. Use #lo() or #hi()"), x); return 1; @@ -972,9 +1149,12 @@ msp430_srcoperand (struct msp430_operand_s * op, /* Now check constants. */ /* Substitute register mode with a constant generator if applicable. */ - x = (short) x; /* Extend sign. */ + if (!allow_20bit_values) + x = (short) x; /* Extend sign. */ - if (x == 0) + if (! constants_allowed) + ; + else if (x == 0) { op->reg = 3; op->am = 0; @@ -1126,9 +1306,17 @@ msp430_srcoperand (struct msp430_operand_s * op, { int x = op->exp.X_add_number; - if (x > 65535 || x < -32768) + if (allow_20bit_values) + { + if (x > 0xfffff || x < -(0x7ffff)) + { + as_bad (_("value 0x%x out of extended range."), x); + return 1; + } + } + else if (x > 65535 || x < -32768) { - as_bad (_("value out of range: %d"), x); + as_bad (_("value out of range: 0x%x"), x); return 1; } } @@ -1160,31 +1348,16 @@ msp430_srcoperand (struct msp430_operand_s * op, } t++; - if (*t != 'r' && *t != 'R') - { - as_bad (_("unknown addressing mode %s"), l); - return 1; - } - t++; /* Points to the reg value. */ - - if (check_reg (t)) + if ((op->reg = check_reg (t)) == -1) { - as_bad (_("Bad register name r%s"), t); + as_bad (_("Bad register name %s"), t); return 1; } op->mode = OP_REG; op->am = m ? 3 : 2; op->ol = 0; - if (m) - *m = 0; /* strip '+' */ - op->reg = atoi (t); - if (op->reg < 0 || op->reg > 15) - { - as_bad (_("MSP430 does not have %d registers"), op->reg); - return 1; - } return 0; } @@ -1209,48 +1382,21 @@ msp430_srcoperand (struct msp430_operand_s * op, t = h; op->am = 1; op->ol = 1; - /* Extract a register. */ - t++; /* Advance pointer. */ - if (*t != 'r' && *t != 'R') + /* Extract a register. */ + if ((op->reg = check_reg (t + 1)) == -1) { as_bad (_ ("unknown operator %s. Did you mean X(Rn) or #[hl][hl][oi](CONST) ?"), l); return 1; } - t++; - op->reg = *t - '0'; - if (op->reg > 9 || op->reg < 0) + if (op->reg == 2) { - as_bad (_("unknown operator (r%s substituted as a register name"), - t); + as_bad (_("r2 should not be used in indexed addressing mode")); return 1; } - t++; - if (*t != ')') - { - op->reg = op->reg * 10; - op->reg += *t - '0'; - - if (op->reg > 15) - { - as_bad (_("unknown operator %s"), l); - return 1; - } - if (op->reg == 2) - { - as_bad (_("r2 should not be used in indexed addressing mode")); - return 1; - } - - if (*(t + 1) != ')') - { - as_bad (_("unknown operator %s"), l); - return 1; - } - } /* Extract constant. */ __tl = l; @@ -1261,9 +1407,17 @@ msp430_srcoperand (struct msp430_operand_s * op, { int x = op->exp.X_add_number; - if (x > 65535 || x < -32768) + if (allow_20bit_values) + { + if (x > 0xfffff || x < - (0x7ffff)) + { + as_bad (_("value 0x%x out of extended range."), x); + return 1; + } + } + else if (x > 65535 || x < -32768) { - as_bad (_("value out of range: %d"), x); + as_bad (_("value out of range: 0x%x"), x); return 1; } @@ -1292,37 +1446,22 @@ msp430_srcoperand (struct msp430_operand_s * op, } while (0); - /* Register mode 'mov r1,r2'. */ - do + /* Possibly register mode 'mov r1,r2'. */ + if ((op->reg = check_reg (l)) != -1) { - char *t = l; - - /* Operand should be a register. */ - if (*t == 'r' || *t == 'R') - { - int x = atoi (t + 1); - - if (check_reg (t + 1)) - break; - - if (x < 0 || x > 15) - break; /* Symbolic mode. */ - - op->mode = OP_REG; - op->am = 0; - op->ol = 0; - op->reg = x; - return 0; - } + op->mode = OP_REG; + op->am = 0; + op->ol = 0; + return 0; } - while (0); /* Symbolic mode 'mov a, b' == 'mov x(pc), y(pc)'. */ do { op->mode = OP_EXP; op->reg = 0; /* PC relative... be careful. */ - op->am = 1; + /* An expression starting with a minus sign is a constant, not an address. */ + op->am = (*l == '-' ? 3 : 1); op->ol = 1; __tl = l; parse_exp (__tl, &(op->exp)); @@ -1337,10 +1476,16 @@ msp430_srcoperand (struct msp430_operand_s * op, static int -msp430_dstoperand (struct msp430_operand_s * op, char * l, int bin) +msp430_dstoperand (struct msp430_operand_s * op, + char * l, + int bin, + bfd_boolean allow_20bit_values, + bfd_boolean constants_allowed) { int dummy; - int ret = msp430_srcoperand (op, l, bin, & dummy); + int ret = msp430_srcoperand (op, l, bin, & dummy, + allow_20bit_values, + constants_allowed); if (ret) return ret; @@ -1373,6 +1518,265 @@ msp430_dstoperand (struct msp430_operand_s * op, char * l, int bin) } +/* Attempt to encode a MOVA instruction with the given operands. + Returns the length of the encoded instruction if successful + or 0 upon failure. If the encoding fails, an error message + will be returned if a pointer is provided. */ + +static int +try_encode_mova (bfd_boolean imm_op, + int bin, + struct msp430_operand_s * op1, + struct msp430_operand_s * op2, + const char ** error_message_return) +{ + short ZEROS = 0; + char *frag; + int where; + + /* Only a restricted subset of the normal MSP430 addressing modes + are supported here, so check for the ones that are allowed. */ + if (imm_op) + { + if (op1->mode == OP_EXP) + { + if (op2->mode != OP_REG) + { + if (error_message_return != NULL) + * error_message_return = _("expected register as second argument of %s"); + return 0; + } + + if (op1->am == 3) + { + /* MOVA #imm20, Rdst. */ + bin |= 0x80 | op2->reg; + frag = frag_more (4); + where = frag - frag_now->fr_literal; + if (op1->exp.X_op == O_constant) + { + bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8; + bfd_putl16 ((bfd_vma) bin, frag); + bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2); + } + else + { + bfd_putl16 ((bfd_vma) bin, frag); + fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_SRC); + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + } + + return 4; + } + else if (op1->am == 1) + { + /* MOVA z16(Rsrc), Rdst. */ + bin |= 0x30 | (op1->reg << 8) | op2->reg; + frag = frag_more (4); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + if (op1->exp.X_op == O_constant) + { + if (op1->exp.X_add_number > 0xffff + || op1->exp.X_add_number < -(0x7fff)) + { + if (error_message_return != NULL) + * error_message_return = _("index value too big for %s"); + return 0; + } + bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2); + } + else + { + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + fix_new_exp (frag_now, where + 2, 2, &(op1->exp), FALSE, + op1->reg == 0 ? + BFD_RELOC_MSP430X_PCR16 : + BFD_RELOC_MSP430X_ABS16); + } + return 4; + } + + if (error_message_return != NULL) + * error_message_return = _("unexpected addressing mode for %s"); + return 0; + } + else if (op1->am == 0) + { + /* MOVA Rsrc, ... */ + if (op2->mode == OP_REG) + { + bin |= 0xc0 | (op1->reg << 8) | op2->reg; + frag = frag_more (2); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + return 2; + } + else if (op2->am == 1) + { + if (op2->reg == 2) + { + /* MOVA Rsrc, &abs20. */ + bin |= 0x60 | (op1->reg << 8); + frag = frag_more (4); + where = frag - frag_now->fr_literal; + if (op2->exp.X_op == O_constant) + { + bin |= (op2->exp.X_add_number >> 16) & 0xf; + bfd_putl16 ((bfd_vma) bin, frag); + bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2); + } + else + { + bfd_putl16 ((bfd_vma) bin, frag); + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + fix_new_exp (frag_now, where, 4, &(op2->exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_DST); + } + return 4; + } + + /* MOVA Rsrc, z16(Rdst). */ + bin |= 0x70 | (op1->reg << 8) | op2->reg; + frag = frag_more (4); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + if (op2->exp.X_op == O_constant) + { + if (op2->exp.X_add_number > 0xffff + || op2->exp.X_add_number < -(0x7fff)) + { + if (error_message_return != NULL) + * error_message_return = _("index value too big for %s"); + return 0; + } + bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2); + } + else + { + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + fix_new_exp (frag_now, where + 2, 2, &(op2->exp), FALSE, + op2->reg == 0 ? + BFD_RELOC_MSP430X_PCR16 : + BFD_RELOC_MSP430X_ABS16); + } + return 4; + } + + if (error_message_return != NULL) + * error_message_return = _("unexpected addressing mode for %s"); + return 0; + } + } + + /* imm_op == FALSE. */ + + if (op1->reg == 2 && op1->am == 1 && op1->mode == OP_EXP) + { + /* MOVA &abs20, Rdst. */ + if (op2->mode != OP_REG) + { + if (error_message_return != NULL) + * error_message_return = _("expected register as second argument of %s"); + return 0; + } + + if (op2->reg == 2 || op2->reg == 3) + { + if (error_message_return != NULL) + * error_message_return = _("constant generator destination register found in %s"); + return 0; + } + + bin |= 0x20 | op2->reg; + frag = frag_more (4); + where = frag - frag_now->fr_literal; + if (op1->exp.X_op == O_constant) + { + bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8; + bfd_putl16 ((bfd_vma) bin, frag); + bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2); + } + else + { + bfd_putl16 ((bfd_vma) bin, frag); + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_SRC); + } + return 4; + } + else if (op1->mode == OP_REG) + { + if (op1->am == 3) + { + /* MOVA @Rsrc+, Rdst. */ + if (op2->mode != OP_REG) + { + if (error_message_return != NULL) + * error_message_return = _("expected register as second argument of %s"); + return 0; + } + + if (op2->reg == 2 || op2->reg == 3) + { + if (error_message_return != NULL) + * error_message_return = _("constant generator destination register found in %s"); + return 0; + } + + if (op1->reg == 2 || op1->reg == 3) + { + if (error_message_return != NULL) + * error_message_return = _("constant generator source register found in %s"); + return 0; + } + + bin |= 0x10 | (op1->reg << 8) | op2->reg; + frag = frag_more (2); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + return 2; + } + else if (op1->am == 2) + { + /* MOVA @Rsrc,Rdst */ + if (op2->mode != OP_REG) + { + if (error_message_return != NULL) + * error_message_return = _("expected register as second argument of %s"); + return 0; + } + + if (op2->reg == 2 || op2->reg == 3) + { + if (error_message_return != NULL) + * error_message_return = _("constant generator destination register found in %s"); + return 0; + } + + if (op1->reg == 2 || op1->reg == 3) + { + if (error_message_return != NULL) + * error_message_return = _("constant generator source register found in %s"); + return 0; + } + + bin |= (op1->reg << 8) | op2->reg; + frag = frag_more (2); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + return 2; + } + } + + if (error_message_return != NULL) + * error_message_return = _("unexpected addressing mode for %s"); + + return 0; +} + /* Parse instruction operands. Return binary opcode. */ @@ -1380,7 +1784,7 @@ static unsigned int msp430_operands (struct msp430_opcode_s * opcode, char * line) { int bin = opcode->bin_opcode; /* Opcode mask. */ - int __is = 0; + int insn_length = 0; char l1[MAX_OP_LEN], l2[MAX_OP_LEN]; char *frag; int where; @@ -1388,6 +1792,14 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) int res = 0; static short ZEROS = 0; int byte_op, imm_op; + int op_length = 0; + int fmt; + int extended = 0x1800; + bfd_boolean extended_op = FALSE; + bfd_boolean addr_op; + const char * error_message; + static signed int repeat_count = 0; + bfd_boolean fix_emitted; /* Opcode is the one from opcodes table line contains something like @@ -1404,11 +1816,22 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) else byte_op = 0; - /* skip .[bwBW]. */ + /* "Address" ops work on 20-bit values. */ + if (*line == '.' && TOLOWER (*(line + 1)) == 'a') + { + addr_op = TRUE; + bin |= BYTE_OPERATION; + } + else + addr_op = FALSE; + + /* skip .[aAbwBW]. */ while (! ISSPACE (*line) && *line) line++; - if (opcode->insn_opnumb && (!*line || *line == '\n')) + if (opcode->fmt != -1 + && opcode->insn_opnumb + && (!*line || *line == '\n')) { as_bad (_("instruction %s requires %d operand(s)"), opcode->name, opcode->insn_opnumb); @@ -1422,204 +1845,971 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) imm_op = 0; - switch (opcode->fmt) + if ((fmt = opcode->fmt) < 0) + { + if (msp430_mcu->isa != MSP430X_ISA) + { + as_bad (_("instruction %s requires MSP430X mcu"), + opcode->name); + return 0; + } + + fmt = (-fmt) - 1; + extended_op = TRUE; + } + + if (repeat_count) + { + /* If requested set the extended instruction repeat count. */ + if (extended_op) + { + if (repeat_count > 0) + extended |= (repeat_count - 1); + else + extended |= (1 << 7) | (- repeat_count); + } + else + as_bad (_("unable to repeat %s insn"), opcode->name); + + repeat_count = 0; + } + + switch (fmt) { case 0: /* Emulated. */ switch (opcode->insn_opnumb) { case 0: /* Set/clear bits instructions. */ - __is = 2; - frag = frag_more (__is); + if (extended_op) + { + if (!addr_op) + extended |= BYTE_OPERATION; + + /* Emit the extension word. */ + insn_length += 2; + frag = frag_more (insn_length); + bfd_putl16 (extended, frag); + } + insn_length += 2; + frag = frag_more (insn_length); bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (__is); + + if (gen_interrupt_nops + && msp430_mcu->isa == MSP430_ISA_54 + && (strcmp (opcode->name, "eint") == 0 + || strcmp (opcode->name, "dint") == 0)) + { + /* Emit a NOP following interrupt enable/disable. + See 1.3.4.1 of the MSP430x5xx User Guide. */ + insn_length += 2; + frag = frag_more (2); + bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag); + } + + dwarf2_emit_insn (insn_length); break; + case 1: /* Something which works with destination operand. */ line = extract_operand (line, l1, sizeof (l1)); - res = msp430_dstoperand (&op1, l1, opcode->bin_opcode); + res = msp430_dstoperand (&op1, l1, opcode->bin_opcode, extended_op, TRUE); if (res) break; - bin |= (op1.reg | (op1.am << 7)); - __is = 1 + op1.ol; - frag = frag_more (2 * __is); + /* Compute the entire instruction length, in bytes. */ + insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2); + frag = frag_more (insn_length); where = frag - frag_now->fr_literal; - bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2 * __is); - if (op1.mode == OP_EXP) + if (extended_op) { - where += 2; - bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + if (!addr_op) + extended |= BYTE_OPERATION; + + if (op1.ol != 0 && ((extended & 0xf) != 0)) + { + as_bad (_("repeat instruction used with non-register mode instruction")); + extended &= ~ 0xf; + } - if (op1.reg) - fix_new_exp (frag_now, where, 2, - &(op1.exp), FALSE, CHECK_RELOC_MSP430); + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7; + + else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_EXT_SRC); + else + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_PCR20_EXT_SRC); + } + + /* Emit the extension word. */ + bfd_putl16 (extended, frag); + frag += 2; + where += 2; + } + + bin |= (op1.reg | (op1.am << 7)); + bfd_putl16 ((bfd_vma) bin, frag); + frag += 2; + where += 2; + + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + { + bfd_putl16 (op1.exp.X_add_number & 0xffff, frag); + } else - fix_new_exp (frag_now, where, 2, - &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op1.reg) + fix_new_exp (frag_now, where, 2, + &(op1.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + } + + if (gen_interrupt_nops + && msp430_mcu->isa == MSP430_ISA_54 + && strcmp (opcode->name, "clr") == 0 + && bin == 0x4302 /* CLR R2*/) + { + /* Emit a NOP following interrupt enable/disable. + See 1.3.4.1 of the MSP430x5xx User Guide. */ + insn_length += 2; + frag = frag_more (2); + bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag); } + + dwarf2_emit_insn (insn_length); break; case 2: + /* Shift instruction. */ + line = extract_operand (line, l1, sizeof (l1)); + strncpy (l2, l1, sizeof (l2)); + l2[sizeof (l2) - 1] = '\0'; + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE); + res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE); + + if (res) + break; /* An error occurred. All warnings were done before. */ + + insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2) + (op2.ol * 2); + frag = frag_more (insn_length); + where = frag - frag_now->fr_literal; + + if (extended_op) + { + if (!addr_op) + extended |= BYTE_OPERATION; + + if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0)) + { + as_bad (_("repeat instruction used with non-register mode instruction")); + extended &= ~ 0xf; + } + + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7; + + else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_EXT_SRC); + else + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_PCR20_EXT_SRC); + } + + if (op2.mode == OP_EXP) + { + if (op2.exp.X_op == O_constant) + extended |= (op2.exp.X_add_number >> 16) & 0xf; + + else if (op1.mode == OP_EXP) + fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE, + op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST + : BFD_RELOC_MSP430X_PCR20_EXT_ODST); + else + fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE, + op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST + : BFD_RELOC_MSP430X_PCR20_EXT_DST); + } + + /* Emit the extension word. */ + bfd_putl16 (extended, frag); + frag += 2; + where += 2; + } + + bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7)); + bfd_putl16 ((bfd_vma) bin, frag); + frag += 2; + where += 2; + + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + { + bfd_putl16 (op1.exp.X_add_number & 0xffff, frag); + } + else + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 2, + &(op1.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + frag += 2; + where += 2; + } + + if (op2.mode == OP_EXP) + { + if (op2.exp.X_op == O_constant) + { + bfd_putl16 (op2.exp.X_add_number & 0xffff, frag); + } + else + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op2.reg) /* Not PC relative. */ + fix_new_exp (frag_now, where, 2, + &(op2.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + } + + dwarf2_emit_insn (insn_length); + break; + + case 3: + /* Branch instruction => mov dst, r0. */ + if (extended_op) + { + as_bad ("Internal error: state 0/3 not coded for extended instructions"); + return 0; + } + + line = extract_operand (line, l1, sizeof (l1)); + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, FALSE); + if (res) + break; + + byte_op = 0; + imm_op = 0; + bin |= ((op1.reg << 8) | (op1.am << 4)); + op_length = 2 + 2 * op1.ol; + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + { + bfd_putl16 (op1.exp.X_add_number & 0xffff, frag + 2); + } + else + { + where += 2; + + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + + if (op1.reg || (op1.reg == 0 && op1.am == 3)) + fix_new_exp (frag_now, where, 2, + &(op1.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + + dwarf2_emit_insn (insn_length + op_length); + break; + + case 4: + /* CALLA instructions. */ + fix_emitted = FALSE; + + line = extract_operand (line, l1, sizeof (l1)); + imm_op = 0; + + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, + extended_op, FALSE); + if (res) + break; + + byte_op = 0; + + op_length = 2 + 2 * op1.ol; + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + + if (imm_op) + { + if (op1.am == 3) + { + bin |= 0xb0; + + fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_DST); + fix_emitted = TRUE; + } + else if (op1.am == 1) + { + if (op1.reg == 0) + { + bin |= 0x90; + + fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_PCR20_CALL); + fix_emitted = TRUE; + } + else + bin |= 0x50 | op1.reg; + } + else if (op1.am == 0) + bin |= 0x40 | op1.reg; + } + else if (op1.am == 1) + { + bin |= 0x80; + + fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_DST); + fix_emitted = TRUE; + } + else if (op1.am == 2) + bin |= 0x60 | op1.reg; + else if (op1.am == 3) + bin |= 0x70 | op1.reg; + + bfd_putl16 ((bfd_vma) bin, frag); + + if (op1.mode == OP_EXP) + { + if (op1.ol != 1) + { + as_bad ("Internal error: unexpected CALLA instruction length: %d\n", op1.ol); + return 0; + } + + bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + + if (! fix_emitted) + fix_new_exp (frag_now, where + 2, 2, + &(op1.exp), FALSE, BFD_RELOC_16); + } + + dwarf2_emit_insn (insn_length + op_length); + break; + + case 5: { - /* Shift instruction. */ + int n; + int reg; + + /* [POP|PUSH]M[.A] #N, Rd */ line = extract_operand (line, l1, sizeof (l1)); - strncpy (l2, l1, sizeof (l2)); - l2[sizeof (l2) - 1] = '\0'; - res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op); - res += msp430_dstoperand (&op2, l2, opcode->bin_opcode); + line = extract_operand (line, l2, sizeof (l2)); - if (res) - break; /* An error occurred. All warnings were done before. */ + if (*l1 != '#') + { + as_bad (_("expected #n as first argument of POPM")); + return 0; + } + parse_exp (l1 + 1, &(op1.exp)); + if (op1.exp.X_op != O_constant) + { + as_bad (_("expected constant expression for first argument of %s"), + opcode->name); + return 0; + } - bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7)); + if ((reg = check_reg (l2)) == -1) + { + as_bad (_("expected register as second argument of %s"), + opcode->name); + return 0; + } - __is = 1 + op1.ol + op2.ol; /* insn size in words. */ - frag = frag_more (2 * __is); + op_length = 2; + frag = frag_more (op_length); where = frag - frag_now->fr_literal; + bin = opcode->bin_opcode; + if (! addr_op) + bin |= 0x100; + n = op1.exp.X_add_number; + bin |= (n - 1) << 4; + if (strcmp (opcode->name, "pushm") == 0) + bin |= reg; + else + { + if (reg - n + 1 < 0) + { + as_bad (_("Too many registers popped")); + return 0; + } + bin |= (reg - n + 1); + } + bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2 * __is); - - if (op1.mode == OP_EXP) + dwarf2_emit_insn (op_length); + break; + } + + case 6: + { + int n; + int reg; + + /* Bit rotation instructions. RRCM, RRAM, RRUM, RLAM. */ + if (extended & 0xff) + { + as_bad (_("repeat count cannot be used with %s"), opcode->name); + return 0; + } + + line = extract_operand (line, l1, sizeof (l1)); + line = extract_operand (line, l2, sizeof (l2)); + + if (*l1 != '#') + { + as_bad (_("expected #n as first argument of %s"), opcode->name); + return 0; + } + parse_exp (l1 + 1, &(op1.exp)); + if (op1.exp.X_op != O_constant) + { + as_bad (_("expected constant expression for first argument of %s"), + opcode->name); + return 0; + } + n = op1.exp.X_add_number; + if (n > 4 || n < 1) { - where += 2; /* Advance 'where' as we do not know _where_. */ - bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + as_bad (_("expected first argument of %s to be in the range 1-4"), + opcode->name); + return 0; + } - if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ - fix_new_exp (frag_now, where, 2, - &(op1.exp), FALSE, CHECK_RELOC_MSP430); - else - fix_new_exp (frag_now, where, 2, - &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + if ((reg = check_reg (l2)) == -1) + { + as_bad (_("expected register as second argument of %s"), + opcode->name); + return 0; } - if (op2.mode == OP_EXP) + op_length = 2; + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + + bin = opcode->bin_opcode; + if (! addr_op) + bin |= 0x10; + bin |= (n - 1) << 10; + bin |= reg; + + bfd_putl16 ((bfd_vma) bin, frag); + dwarf2_emit_insn (op_length); + break; + } + + case 7: + { + int reg; + + /* RRUX: Synthetic unsigned right shift of a register by one bit. */ + if (extended & 0xff) { - imm_op = 0; - bfd_putl16 ((bfd_vma) ZEROS, frag + 2 + ((__is == 3) ? 2 : 0)); + as_bad (_("repeat count cannot be used with %s"), opcode->name); + return 0; + } - if (op2.reg) /* Not PC relative. */ - fix_new_exp (frag_now, where + 2, 2, - &(op2.exp), FALSE, CHECK_RELOC_MSP430); - else - fix_new_exp (frag_now, where + 2, 2, - &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + line = extract_operand (line, l1, sizeof (l1)); + if ((reg = check_reg (l1)) == -1) + { + as_bad (_("expected register as argument of %s"), + opcode->name); + return 0; + } + + if (byte_op) + { + /* Tricky - there is no single instruction that will do this. + Encode as: RRA.B rN { BIC.B #0x80, rN */ + op_length = 6; + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + bin = 0x1140 | reg; + bfd_putl16 ((bfd_vma) bin, frag); + dwarf2_emit_insn (2); + bin = 0xc070 | reg; + bfd_putl16 ((bfd_vma) bin, frag + 2); + bin = 0x0080; + bfd_putl16 ((bfd_vma) bin, frag + 4); + dwarf2_emit_insn (4); + } + else + { + /* Encode as RRUM[.A] rN. */ + bin = opcode->bin_opcode; + if (! addr_op) + bin |= 0x10; + bin |= reg; + op_length = 2; + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + bfd_putl16 ((bfd_vma) bin, frag); + dwarf2_emit_insn (op_length); } break; } - case 3: - /* Branch instruction => mov dst, r0. */ - line = extract_operand (line, l1, sizeof (l1)); - res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op); - if (res) + case 8: + { + bfd_boolean need_reloc = FALSE; + int n; + int reg; + + /* ADDA, CMPA and SUBA address instructions. */ + if (extended & 0xff) + { + as_bad (_("repeat count cannot be used with %s"), opcode->name); + return 0; + } + + line = extract_operand (line, l1, sizeof (l1)); + line = extract_operand (line, l2, sizeof (l2)); + + bin = opcode->bin_opcode; + + if (*l1 == '#') + { + parse_exp (l1 + 1, &(op1.exp)); + + if (op1.exp.X_op == O_constant) + { + n = op1.exp.X_add_number; + if (n > 0xfffff || n < - (0x7ffff)) + { + as_bad (_("expected value of first argument of %s to fit into 20-bits"), + opcode->name); + return 0; + } + + bin |= ((n >> 16) & 0xf) << 8; + } + else + { + n = 0; + need_reloc = TRUE; + } + + op_length = 4; + } + else + { + if ((n = check_reg (l1)) == -1) + { + as_bad (_("expected register name or constant as first argument of %s"), + opcode->name); + return 0; + } + + bin |= (n << 8) | (1 << 6); + op_length = 2; + } + + if ((reg = check_reg (l2)) == -1) + { + as_bad (_("expected register as second argument of %s"), + opcode->name); + return 0; + } + + frag = frag_more (op_length); + where = frag - frag_now->fr_literal; + bin |= reg; + if (need_reloc) + fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_ADR_SRC); + + bfd_putl16 ((bfd_vma) bin, frag); + if (op_length == 4) + bfd_putl16 ((bfd_vma) (n & 0xffff), frag + 2); + dwarf2_emit_insn (op_length); break; + } - byte_op = 0; + case 9: /* MOVA, BRA, RETA. */ imm_op = 0; + bin = opcode->bin_opcode; - bin |= ((op1.reg << 8) | (op1.am << 4)); - __is = 1 + op1.ol; - frag = frag_more (2 * __is); - where = frag - frag_now->fr_literal; - bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2 * __is); + if (strcmp (opcode->name, "reta") == 0) + { + /* The RETA instruction does not take any arguments. + The implicit first argument is @SP+. + The implicit second argument is PC. */ + op1.mode = OP_REG; + op1.am = 3; + op1.reg = 1; + + op2.mode = OP_REG; + op2.reg = 0; + } + else + { + line = extract_operand (line, l1, sizeof (l1)); + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, + &imm_op, extended_op, FALSE); - if (op1.mode == OP_EXP) + if (strcmp (opcode->name, "bra") == 0) + { + /* This is the BRA synthetic instruction. + The second argument is always PC. */ + op2.mode = OP_REG; + op2.reg = 0; + } + else + { + line = extract_operand (line, l2, sizeof (l2)); + res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, + extended_op, TRUE); + } + + if (res) + break; /* Error occurred. All warnings were done before. */ + } + + /* Only a restricted subset of the normal MSP430 addressing modes + are supported here, so check for the ones that are allowed. */ + if ((op_length = try_encode_mova (imm_op, bin, & op1, & op2, + & error_message)) == 0) { - where += 2; - bfd_putl16 ((bfd_vma) ZEROS, frag + 2); + as_bad (error_message, opcode->name); + return 0; + } + dwarf2_emit_insn (op_length); + break; + + case 10: /* RPT */ + line = extract_operand (line, l1, sizeof l1); + /* The RPT instruction only accepted immediates and registers. */ + if (*l1 == '#') + { + parse_exp (l1 + 1, &(op1.exp)); + if (op1.exp.X_op != O_constant) + { + as_bad (_("expected constant value as argument to RPT")); + return 0; + } + if (op1.exp.X_add_number < 1 + || op1.exp.X_add_number > (1 << 4)) + { + as_bad (_("expected constant in the range 2..16")); + return 0; + } + + /* We silently accept and ignore a repeat count of 1. */ + if (op1.exp.X_add_number > 1) + repeat_count = op1.exp.X_add_number; + } + else + { + int reg; - if (op1.reg || (op1.reg == 0 && op1.am == 3)) - fix_new_exp (frag_now, where, 2, - &(op1.exp), FALSE, CHECK_RELOC_MSP430); + if ((reg = check_reg (l1)) != -1) + { + if (reg == 0) + as_warn (_("PC used as an argument to RPT")); + else + repeat_count = - reg; + } else - fix_new_exp (frag_now, where, 2, - &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + { + as_bad (_("expected constant or register name as argument to RPT insn")); + return 0; + } } break; + + default: + as_bad (_("Illegal emulated instruction ")); + break; } break; case 1: /* Format 1, double operand. */ line = extract_operand (line, l1, sizeof (l1)); line = extract_operand (line, l2, sizeof (l2)); - res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op); - res += msp430_dstoperand (&op2, l2, opcode->bin_opcode); + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE); + res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE); if (res) break; /* Error occurred. All warnings were done before. */ - bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7)); + if (extended_op + && strcmp (opcode->name, "movx") == 0 + && addr_op + && msp430_enable_relax) + { + /* This is the MOVX.A instruction. See if we can convert + it into the MOVA instruction instead. This saves 2 bytes. */ + if ((op_length = try_encode_mova (imm_op, 0x0000, & op1, & op2, + NULL)) != 0) + { + dwarf2_emit_insn (op_length); + break; + } + } + + /* Compute the entire length of the instruction in bytes. */ + insn_length = + (extended_op ? 2 : 0) /* The extension word. */ + + 2 /* The opcode */ + + (2 * op1.ol) /* The first operand. */ + + (2 * op2.ol); /* The second operand. */ - __is = 1 + op1.ol + op2.ol; /* insn size in words. */ - frag = frag_more (2 * __is); + frag = frag_more (insn_length); where = frag - frag_now->fr_literal; + + if (extended_op) + { + if (!addr_op) + extended |= BYTE_OPERATION; + + if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0)) + { + as_bad (_("repeat instruction used with non-register mode instruction")); + extended &= ~ 0xf; + } + + /* If necessary, emit a reloc to update the extension word. */ + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7; + + else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_EXT_SRC); + else + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_PCR20_EXT_SRC); + } + + if (op2.mode == OP_EXP) + { + if (op2.exp.X_op == O_constant) + extended |= (op2.exp.X_add_number >> 16) & 0xf; + + else if (op1.mode == OP_EXP) + fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE, + op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST + : BFD_RELOC_MSP430X_PCR20_EXT_ODST); + + else + fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE, + op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST + : BFD_RELOC_MSP430X_PCR20_EXT_DST); + } + + /* Emit the extension word. */ + bfd_putl16 (extended, frag); + where += 2; + frag += 2; + } + + bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7)); bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2 * __is); + where += 2; + frag += 2; if (op1.mode == OP_EXP) { - where += 2; /* Advance where as we do not know _where_. */ - bfd_putl16 ((bfd_vma) ZEROS, frag + 2); - - if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ - fix_new_exp (frag_now, where, 2, - &(op1.exp), FALSE, CHECK_RELOC_MSP430); + if (op1.exp.X_op == O_constant) + { + bfd_putl16 (op1.exp.X_add_number & 0xffff, frag); + } else - fix_new_exp (frag_now, where, 2, - &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 2, + &(op1.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + + where += 2; + frag += 2; } if (op2.mode == OP_EXP) { - imm_op = 0; - bfd_putl16 ((bfd_vma) ZEROS, frag + 2 + ((__is == 3) ? 2 : 0)); - - if (op2.reg) /* Not PC relative. */ - fix_new_exp (frag_now, where + 2, 2, - &(op2.exp), FALSE, CHECK_RELOC_MSP430); + if (op2.exp.X_op == O_constant) + { + bfd_putl16 (op2.exp.X_add_number & 0xffff, frag); + } else - fix_new_exp (frag_now, where + 2, 2, - &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op2.reg) /* Not PC relative. */ + fix_new_exp (frag_now, where, 2, + &(op2.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } + } + + if (gen_interrupt_nops + && msp430_mcu->isa == MSP430_ISA_54 + && ( (strcmp (opcode->name, "bic") == 0 && bin == 0xc232) + || (strcmp (opcode->name, "bis") == 0 && bin == 0xd232) + || (strcmp (opcode->name, "mov") == 0 && op2.mode == OP_REG && op2.reg == 2))) + { + /* Emit a NOP following interrupt enable/disable. + See 1.3.4.1 of the MSP430x5xx User Guide. */ + insn_length += 2; + frag = frag_more (2); + bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag); } + + dwarf2_emit_insn (insn_length); break; case 2: /* Single-operand mostly instr. */ if (opcode->insn_opnumb == 0) { /* reti instruction. */ + insn_length += 2; frag = frag_more (2); bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2); + dwarf2_emit_insn (insn_length); break; } line = extract_operand (line, l1, sizeof (l1)); - res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op); + res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, + &imm_op, extended_op, TRUE); if (res) break; /* Error in operand. */ - bin |= op1.reg | (op1.am << 4); - __is = 1 + op1.ol; - frag = frag_more (2 * __is); + insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2); + frag = frag_more (insn_length); where = frag - frag_now->fr_literal; + + if (extended_op) + { + if (strcmp (opcode->name, "swpbx") == 0 + || strcmp (opcode->name, "sxtx") == 0) + { + /* These two instructions use a special + encoding of the A/L and B/W bits. */ + bin &= ~ BYTE_OPERATION; + + if (byte_op) + { + as_bad (_("%s instruction does not accept a .b suffix"), + opcode->name); + return 0; + } + else if (! addr_op) + extended |= BYTE_OPERATION; + } + else if (! addr_op) + extended |= BYTE_OPERATION; + + if (op1.ol != 0 && ((extended & 0xf) != 0)) + { + as_bad (_("repeat instruction used with non-register mode instruction")); + extended &= ~ 0xf; + } + + if (op1.mode == OP_EXP) + { + if (op1.exp.X_op == O_constant) + extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7; + + else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_ABS20_EXT_SRC); + else + fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE, + BFD_RELOC_MSP430X_PCR20_EXT_SRC); + } + + /* Emit the extension word. */ + bfd_putl16 (extended, frag); + frag += 2; + where += 2; + } + + bin |= op1.reg | (op1.am << 4); bfd_putl16 ((bfd_vma) bin, frag); - dwarf2_emit_insn (2 * __is); + frag += 2; + where += 2; if (op1.mode == OP_EXP) { - bfd_putl16 ((bfd_vma) ZEROS, frag + 2); - - if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ - fix_new_exp (frag_now, where + 2, 2, - &(op1.exp), FALSE, CHECK_RELOC_MSP430); + if (op1.exp.X_op == O_constant) + { + bfd_putl16 (op1.exp.X_add_number & 0xffff, frag); + } else - fix_new_exp (frag_now, where + 2, 2, - &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + { + bfd_putl16 ((bfd_vma) ZEROS, frag); + + if (!extended_op) + { + if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */ + fix_new_exp (frag_now, where, 2, + &(op1.exp), FALSE, CHECK_RELOC_MSP430); + else + fix_new_exp (frag_now, where, 2, + &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL); + } + } } + + dwarf2_emit_insn (insn_length); break; case 3: /* Conditional jumps instructions. */ @@ -1634,7 +2824,6 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) m++; parse_exp (m, &exp); - frag = frag_more (2); /* Instr size is 1 word. */ /* In order to handle something like: @@ -1678,11 +2867,16 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) break; } + insn_length += 2; + frag = frag_more (2); /* Instr size is 1 word. */ + bin |= x & 0x3ff; bfd_putl16 ((bfd_vma) bin, frag); } else if (exp.X_op == O_symbol && *l1 != '$') { + insn_length += 2; + frag = frag_more (2); /* Instr size is 1 word. */ where = frag - frag_now->fr_literal; fix_new_exp (frag_now, where, 2, &exp, TRUE, BFD_RELOC_MSP430_10_PCREL); @@ -1694,11 +2888,9 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) as_bad (_("instruction requires label sans '$'")); } else - { - as_bad (_ - ("instruction requires label or value in range -511:512")); - } - dwarf2_emit_insn (2 * __is); + as_bad (_ + ("instruction requires label or value in range -511:512")); + dwarf2_emit_insn (insn_length); break; } else @@ -1714,7 +2906,7 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) as_bad (_("polymorphs are not enabled. Use -mP option to enable.")); break; } - + line = extract_operand (line, l1, sizeof (l1)); if (l1[0]) { @@ -1731,15 +2923,20 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) /* Relaxation required. */ struct rcodes_s rc = msp430_rcodes[opcode->insn_opnumb]; - /* The parameter to dwarf2_emit_insn is actually the offset to the start - of the insn from the fix piece of instruction that was emitted. - Since next fragments may have variable size we tie debug info - to the beginning of the instruction. */ + if (msp430_mcu->isa == MSP430X_ISA) + rc = msp430x_rcodes[opcode->insn_opnumb]; + + /* The parameter to dwarf2_emit_insn is actually the offset to + the start of the insn from the fix piece of instruction that + was emitted. Since next fragments may have variable size we + tie debug info to the beginning of the instruction. */ + insn_length += 8; frag = frag_more (8); dwarf2_emit_insn (0); bfd_putl16 ((bfd_vma) rc.sop, frag); frag = frag_variant (rs_machine_dependent, 8, 2, - ENCODE_RELAX (rc.lpos, STATE_BITS10), /* Wild guess. */ + /* Wild guess. */ + ENCODE_RELAX (rc.lpos, STATE_BITS10), exp.X_add_symbol, 0, /* Offset is zero if jump dist less than 1K. */ (char *) frag); @@ -1772,6 +2969,10 @@ msp430_operands (struct msp430_opcode_s * opcode, char * line) /* Relaxation required. */ struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb]; + if (msp430_mcu->isa == MSP430X_ISA) + hc = msp430x_hcodes[opcode->insn_opnumb]; + + insn_length += 8; frag = frag_more (8); dwarf2_emit_insn (0); bfd_putl16 ((bfd_vma) hc.op0, frag); @@ -1866,16 +3067,22 @@ md_pcrel_from_section (fixS * fixp, segT sec) /* Replaces standard TC_FORCE_RELOCATION_LOCAL. Now it handles the situation when relocations - have to be passed to linker. */ + have to be passed to linker. */ int -msp430_force_relocation_local(fixS *fixp) +msp430_force_relocation_local (fixS *fixp) { + if (fixp->fx_r_type == BFD_RELOC_MSP430_10_PCREL) + return 1; + + if (fixp->fx_pcrel) + return 1; + if (msp430_enable_polys && !msp430_enable_relax) return 1; - else - return (!fixp->fx_pcrel - || generic_force_reloc(fixp)); + + return (!fixp->fx_pcrel + || generic_force_reloc (fixp)); } @@ -1928,23 +3135,17 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) value -= S_GET_VALUE (fixp->fx_subsy); fixp->fx_done = 1; } - else - { - /* We don't actually support subtracting a symbol. */ - as_bad_where (fixp->fx_file, fixp->fx_line, - _("expression too complex")); - } } } fixp->fx_no_overflow = 1; - /* if polymorphs are enabled and relax disabled. - do not kill any relocs and pass them to linker. */ - if (msp430_enable_polys + /* If polymorphs are enabled and relax disabled. + do not kill any relocs and pass them to linker. */ + if (msp430_enable_polys && !msp430_enable_relax) { - if (!fixp->fx_addsy || (fixp->fx_addsy + if (!fixp->fx_addsy || (fixp->fx_addsy && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section)) fixp->fx_done = 1; /* It is ok to kill 'abs' reloc. */ else @@ -1955,7 +3156,6 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) { /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ - where = (unsigned char *) fixp->fx_frag->fr_literal + fixp->fx_where; insn = bfd_getl16 (where); @@ -1979,27 +3179,25 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) bfd_putl16 ((bfd_vma) (value | insn), where); break; + case BFD_RELOC_MSP430X_PCR16: case BFD_RELOC_MSP430_RL_PCREL: case BFD_RELOC_MSP430_16_PCREL: if (value & 1) as_bad_where (fixp->fx_file, fixp->fx_line, _("odd address operand: %ld"), value); - - /* Nothing to be corrected here. */ - if (value < -32768 || value > 65536) - as_bad_where (fixp->fx_file, fixp->fx_line, - _("operand out of range: %ld"), value); - - value &= 0xffff; /* Get rid of extended sign. */ - bfd_putl16 ((bfd_vma) value, where); - break; + /* Fall through. */ case BFD_RELOC_MSP430_16_PCREL_BYTE: /* Nothing to be corrected here. */ if (value < -32768 || value > 65536) as_bad_where (fixp->fx_file, fixp->fx_line, _("operand out of range: %ld"), value); + /* Fall through. */ + case BFD_RELOC_MSP430X_ABS16: + case BFD_RELOC_MSP430_16: + case BFD_RELOC_16: + case BFD_RELOC_MSP430_16_BYTE: value &= 0xffff; /* Get rid of extended sign. */ bfd_putl16 ((bfd_vma) value, where); break; @@ -2008,11 +3206,53 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) bfd_putl16 ((bfd_vma) value, where); break; - case BFD_RELOC_MSP430_16: - case BFD_RELOC_16: - case BFD_RELOC_MSP430_16_BYTE: - value &= 0xffff; - bfd_putl16 ((bfd_vma) value, where); + case BFD_RELOC_MSP430_ABS8: + case BFD_RELOC_8: + bfd_put_8 (NULL, (bfd_vma) value, where); + break; + + case BFD_RELOC_MSP430X_ABS20_EXT_SRC: + case BFD_RELOC_MSP430X_PCR20_EXT_SRC: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4); + value >>= 16; + bfd_putl16 ((bfd_vma) (((value & 0xf) << 7) | insn), where); + break; + + case BFD_RELOC_MSP430X_ABS20_ADR_SRC: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2); + value >>= 16; + bfd_putl16 ((bfd_vma) (((value & 0xf) << 8) | insn), where); + break; + + case BFD_RELOC_MSP430X_ABS20_EXT_ODST: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6); + value >>= 16; + bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where); + break; + + case BFD_RELOC_MSP430X_PCR20_CALL: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2); + value >>= 16; + bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where); + break; + + case BFD_RELOC_MSP430X_ABS20_EXT_DST: + case BFD_RELOC_MSP430X_PCR20_EXT_DST: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4); + value >>= 16; + bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where); + break; + + case BFD_RELOC_MSP430X_PCR20_EXT_ODST: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6); + value >>= 16; + bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where); + break; + + case BFD_RELOC_MSP430X_ABS20_ADR_DST: + bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2); + value >>= 16; + bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where); break; default: @@ -2027,6 +3267,20 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) } } +static bfd_boolean +S_IS_GAS_LOCAL (symbolS * s) +{ + const char * name; + unsigned int len; + + if (s == NULL) + return FALSE; + name = S_GET_NAME (s); + len = strlen (name) - 1; + + return name[len] == 1 || name[len] == 2; +} + /* GAS will call this to generate a reloc, passing the resulting reloc to `bfd_install_relocation'. This currently works poorly, as `bfd_install_relocation' often does the wrong thing, and instances of @@ -2036,33 +3290,157 @@ md_apply_fix (fixS * fixp, valueT * valuep, segT seg) /* If while processing a fixup, a reloc really needs to be created then it is done here. */ -arelent * +arelent ** tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp) { - arelent * reloc; + static arelent * no_relocs = NULL; + static arelent * relocs[MAX_RELOC_EXPANSION + 1]; + arelent *reloc; reloc = xmalloc (sizeof (arelent)); - - reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); - *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); - reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == (reloc_howto_type *) NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, _("reloc %d not supported by object file format"), (int) fixp->fx_r_type); - return NULL; + free (reloc); + return & no_relocs; + } + + relocs[0] = reloc; + relocs[1] = NULL; + + if (fixp->fx_subsy + && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section) + { + fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy); + fixp->fx_subsy = NULL; } - if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT - || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) - reloc->address = fixp->fx_offset; + if (fixp->fx_addsy && fixp->fx_subsy) + { + asection *asec, *ssec; + + asec = S_GET_SEGMENT (fixp->fx_addsy); + ssec = S_GET_SEGMENT (fixp->fx_subsy); + + /* If we have a difference between two different, non-absolute symbols + we must generate two relocs (one for each symbol) and allow the + linker to resolve them - relaxation may change the distances between + symbols, even local symbols defined in the same section. + + Unfortunately we cannot do this with assembler generated local labels + because there can be multiple incarnations of the same label, with + exactly the same name, in any given section and the linker will have + no way to identify the correct one. Instead we just have to hope + that no relaxtion will occur between the local label and the other + symbol in the expression. + + Similarly we have to compute differences between symbols in the .eh_frame + section as the linker is not smart enough to apply relocations there + before attempting to process it. */ + if ((ssec != absolute_section || asec != absolute_section) + && (fixp->fx_addsy != fixp->fx_subsy) + && strcmp (ssec->name, ".eh_frame") != 0 + && ! S_IS_GAS_LOCAL (fixp->fx_addsy) + && ! S_IS_GAS_LOCAL (fixp->fx_subsy)) + { + arelent * reloc2 = xmalloc (sizeof * reloc); - reloc->addend = fixp->fx_offset; + relocs[0] = reloc2; + relocs[1] = reloc; - return reloc; + reloc2->address = reloc->address; + reloc2->howto = bfd_reloc_type_lookup (stdoutput, + BFD_RELOC_MSP430_SYM_DIFF); + reloc2->addend = - S_GET_VALUE (fixp->fx_subsy); + + if (ssec == absolute_section) + reloc2->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; + else + { + reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy); + } + + reloc->addend = fixp->fx_offset; + if (asec == absolute_section) + { + reloc->addend += S_GET_VALUE (fixp->fx_addsy); + reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr; + } + else + { + reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + } + + fixp->fx_pcrel = 0; + fixp->fx_done = 1; + return relocs; + } + else + { + char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal; + + reloc->addend = (S_GET_VALUE (fixp->fx_addsy) + - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset); + + switch (fixp->fx_r_type) + { + case BFD_RELOC_8: + md_number_to_chars (fixpos, reloc->addend, 1); + break; + + case BFD_RELOC_16: + md_number_to_chars (fixpos, reloc->addend, 2); + break; + + case BFD_RELOC_24: + md_number_to_chars (fixpos, reloc->addend, 3); + break; + + case BFD_RELOC_32: + md_number_to_chars (fixpos, reloc->addend, 4); + break; + + default: + reloc->sym_ptr_ptr + = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr; + return relocs; + } + + free (reloc); + return & no_relocs; + } + } + else + { +#if 0 + if (fixp->fx_r_type == BFD_RELOC_MSP430X_ABS16 + && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section) + { + bfd_vma amount = S_GET_VALUE (fixp->fx_addsy); + char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal; + + md_number_to_chars (fixpos, amount, 2); + free (reloc); + return & no_relocs; + } +#endif + reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->addend = fixp->fx_offset; + + if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + reloc->address = fixp->fx_offset; + } + + return relocs; } int @@ -2122,6 +3500,8 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_UNDEF): /* Convert uncond branch jmp lab -> br lab. */ cc = & msp430_rcodes[7]; + if (msp430_mcu->isa == MSP430X_ISA) + cc = msp430x_rcodes + 7; where = fragP->fr_literal + fragP->fr_fix; bfd_putl16 (cc->lop0, where); rela = BFD_RELOC_MSP430_RL_PCREL; @@ -2136,9 +3516,19 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, insn &= 0xffff; /* Find actual instruction. */ - for (i = 0; i < 7 && !cc; i++) - if (msp430_rcodes[i].sop == insn) - cc = & msp430_rcodes[i]; + if (msp430_mcu->isa == MSP430X_ISA) + { + for (i = 0; i < 7 && !cc; i++) + if (msp430x_rcodes[i].sop == insn) + cc = msp430x_rcodes + i; + } + else + { + for (i = 0; i < 7 && !cc; i++) + if (msp430_rcodes[i].sop == insn) + cc = & msp430_rcodes[i]; + } + if (!cc || !cc->name) as_fatal (_("internal inconsistency problem in %s: insn %04lx"), __FUNCTION__, (long) insn); @@ -2153,6 +3543,10 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_WORD): case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_UNDEF): cc = & msp430_rcodes[6]; + + if (msp430_mcu->isa == MSP430X_ISA) + cc = msp430x_rcodes + 6; + where = fragP->fr_literal + fragP->fr_fix; bfd_putl16 (cc->lop0, where); bfd_putl16 (cc->lop1, where + 2); @@ -2166,9 +3560,19 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, int insn = bfd_getl16 (fragP->fr_opcode + 2); insn &= 0xffff; - for (i = 0; i < 4 && !hc; i++) - if (msp430_hcodes[i].op1 == insn) - hc = &msp430_hcodes[i]; + if (msp430_mcu->isa == MSP430X_ISA) + { + for (i = 0; i < 4 && !hc; i++) + if (msp430x_hcodes[i].op1 == insn) + hc = msp430x_hcodes + i; + } + else + { + for (i = 0; i < 4 && !hc; i++) + if (msp430_hcodes[i].op1 == insn) + hc = &msp430_hcodes[i]; + } + if (!hc || !hc->name) as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"), __FUNCTION__, (long) insn); @@ -2177,7 +3581,7 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, another fix will be applied to the next word of insn anyway. */ if (hc->tlab == 2) fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, - fragP->fr_offset, TRUE, rela); + fragP->fr_offset, TRUE, rela); fragP->fr_fix += 2; } @@ -2189,9 +3593,18 @@ md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, int insn = bfd_getl16 (fragP->fr_opcode + 2); insn &= 0xffff; - for (i = 0; i < 4 && !hc; i++) - if (msp430_hcodes[i].op1 == insn) - hc = & msp430_hcodes[i]; + if (msp430_mcu->isa == MSP430X_ISA) + { + for (i = 0; i < 4 && !hc; i++) + if (msp430x_hcodes[i].op1 == insn) + hc = msp430x_hcodes + i; + } + else + { + for (i = 0; i < 4 && !hc; i++) + if (msp430_hcodes[i].op1 == insn) + hc = & msp430_hcodes[i]; + } if (!hc || !hc->name) as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"), __FUNCTION__, (long) insn); @@ -2251,10 +3664,10 @@ msp430_relax_frag (segT seg ATTRIBUTE_UNUSED, fragS * fragP, if (!msp430_enable_relax) { /* Relaxation is not enabled. So, make all jump as long ones - by setting 'aim' to quite high value. */ + by setting 'aim' to quite high value. */ aim = 0x7fff; } - + this_state = fragP->fr_subtype; start_type = this_type = table + this_state; @@ -2292,3 +3705,58 @@ msp430_relax_frag (segT seg ATTRIBUTE_UNUSED, fragS * fragP, fragP->fr_subtype = this_state; return growth; } + +/* Return FALSE if the fixup in fixp should be left alone and not + adjusted. We return FALSE here so that linker relaxation will + work. */ + +bfd_boolean +msp430_fix_adjustable (struct fix *fixp ATTRIBUTE_UNUSED) +{ + /* If the symbol is in a non-code section then it should be OK. */ + if (fixp->fx_addsy + && ((S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE) == 0)) + return TRUE; + + return FALSE; +} + +/* Set the contents of the .MSP430.attributes section. */ + +void +msp430_md_end (void) +{ + bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_ISA, + msp430_mcu->isa == MSP430X_ISA ? 2 : 1); + + bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Code_Model, + large_model ? 2 : 1); + + bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Data_Model, + large_model ? 2 : 1); +} + +/* Returns FALSE if there is a msp430 specific reason why the + subtraction of two same-section symbols cannot be computed by + the assembler. */ + +bfd_boolean +msp430_allow_local_subtract (expressionS * left, + expressionS * right, + segT section) +{ + /* If the symbols are not in a code section then they are OK. */ + if ((section->flags & SEC_CODE) == 0) + return TRUE; + + if (S_IS_GAS_LOCAL (left->X_add_symbol) || S_IS_GAS_LOCAL (right->X_add_symbol)) + return TRUE; + + if (left->X_add_symbol == right->X_add_symbol) + return TRUE; + + /* We have to assume that there may be instructions between the + two symbols and that relaxation may increase the distance between + them. */ + return FALSE; +} diff --git a/gas/config/tc-msp430.h b/gas/config/tc-msp430.h index 118b46d..f805f66 100644 --- a/gas/config/tc-msp430.h +++ b/gas/config/tc-msp430.h @@ -1,5 +1,5 @@ /* This file is tc-msp430.h - Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc. + Copyright (C) 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky @@ -99,8 +99,9 @@ extern long md_pcrel_from_section (struct fix *, segT); example, a value of 2 might print `1234 5678' where a value of 1 would print `12 34 56 78'. The default value is 4. */ -#define LEX_DOLLAR 0 -/* MSP430 port does not use `$' as a logical line separator */ +/* Support symbols like: C$$IO$$. */ +#undef LEX_DOLLAR +#define LEX_DOLLAR 1 #define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0 /* An `.lcomm' directive with no explicit alignment parameter will @@ -116,9 +117,52 @@ extern long md_pcrel_from_section (struct fix *, segT); extern long msp430_relax_frag (segT, fragS *, long); #define TC_FORCE_RELOCATION_LOCAL(FIX) \ - msp430_force_relocation_local(FIX) -extern int msp430_force_relocation_local(struct fix *); - + msp430_force_relocation_local (FIX) +extern int msp430_force_relocation_local (struct fix *); extern int msp430_enable_relax; extern int msp430_enable_polys; + +#define tc_fix_adjustable(FIX) msp430_fix_adjustable (FIX) +extern bfd_boolean msp430_fix_adjustable (struct fix *); + +/* Allow hexadeciaml numbers with 'h' suffix. Note that if the number + starts with a letter it will be interpreted as a symbol name not a + constant. Thus "beach" is a symbol not the hex value 0xbeac. So + is A5A5h... */ +#define NUMBERS_WITH_SUFFIX 1 + +#define md_end msp430_md_end +extern void msp430_md_end (void); + +/* Do not allow call frame debug info optimization as otherwise we could + generate the DWARF directives without the relocs necessary to patch + them up. */ +#define md_allow_eh_opt 0 + +/* The difference between same-section symbols may be affected by linker + relaxation, so do not resolve such expressions in the assembler. */ +#define md_allow_local_subtract(l,r,s) msp430_allow_local_subtract (l, r, s) +extern bfd_boolean msp430_allow_local_subtract (expressionS *, expressionS *, segT); + +#define RELOC_EXPANSION_POSSIBLE +#define MAX_RELOC_EXPANSION 2 + +#define DIFF_EXPR_OK + +/* Do not adjust relocations involving symbols in code sections, + because it breaks linker relaxations. This could be fixed in the + linker, but this fix is simpler, and it pretty much only affects + object size a little bit. */ +#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \ + (((SEC)->flags & SEC_CODE) != 0 \ + || ! SEG_NORMAL (SEC) \ + || TC_FORCE_RELOCATION (FIX)) + +/* We validate subtract arguments within tc_gen_reloc(), + so don't report errors at this point. */ +#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1 + +#define DWARF2_USE_FIXED_ADVANCE_PC 1 + +#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_CODE) diff --git a/gas/doc/c-msp430.texi b/gas/doc/c-msp430.texi index 4beb90a..06e4137 100644 --- a/gas/doc/c-msp430.texi +++ b/gas/doc/c-msp430.texi @@ -1,4 +1,4 @@ -@c Copyright 2002, 2004, 2005, 2011 Free Software Foundation, Inc. +@c Copyright 2002-2013 Free Software Foundation, Inc. @c This is part of the GAS manual. @c For copying conditions, see the file as.texinfo. @ifset GENERIC @@ -36,6 +36,20 @@ enables polymorph instructions handler. @item -mQ enables relaxation at assembly time. DANGEROUS! +@item -ml +indicates that the input uses the large code model. + +@item -mN +disables the generation of a NOP instruction following any instruction +that might change the interrupts enabled/disabled state. For the +MSP430x5xx series the instructions: @code{EINT}, @code{DINT}, @code{BIC +#8, SR}, @code{BIS #8, SR} and @code{MOV.W <>, SR} must be followed by +a NOP instruction in order to ensure the correct processing of +interrupts. By default generation of the NOP instruction happens +automatically, but this command line option disables this behaviour. +It is then up to the programmer to ensure that interrupts are enabled +and disabled correctly. + @end table @node MSP430 Syntax diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index ea5d8d3..bd015c6 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,22 @@ +2013-05-02 Nick Clifton + + * gas/all/gas.exp: Skip the DIFF1 test for the MSP430. + Expect the FORWARD test to pass for the MSP430. + Skip the REDEF tests for the MSP430. + Expect the 930509A test to fail for the MSP430. + * gas/all/sleb128-4.d: Skip for the MSP430. + * gas/elf/elf.exp: Set target_machine to msp430 for the MSP430. + Skip the EHOPT0 test for the MSP430. + Skip the REDEF and EQU-RELOC tests for the MSP430. + * gas/elf/section2.e-msp430: New file. + * gas/lns/lns-big-delta.d: Remove expectation of 20-bit + addresses. + * gas/lns/lns.exp: Use alternate LNS COMMON test for the MSP430. + * gas/msp430/msp430x.s: New test. + * gas/msp430/msp430x.d: Expected disassembly. + * gas/msp430/msp430.exp: Run new test. + * gas/msp430/opcode.d: Update expected disassembly. + 2013-04-30 Chao-ying Fu * gas/mips/ext-ill.s: New file. diff --git a/gas/testsuite/gas/all/gas.exp b/gas/testsuite/gas/all/gas.exp index 709b448..f604ac9 100644 --- a/gas/testsuite/gas/all/gas.exp +++ b/gas/testsuite/gas/all/gas.exp @@ -61,6 +61,7 @@ if { ![istarget hppa*-*-*] && ![istarget alpha*-*-*vms*] && ![istarget rx-*-*] && ![istarget mn10300-*-*] + && ![istarget msp430*-*-*] && ![istarget am3*-*-*] } then { gas_test_error "diff1.s" "" "difference of two undefined symbols" } @@ -99,7 +100,7 @@ case $target_triplet in { default { # Some targets don't manage to resolve BFD_RELOC_8 for constants. setup_xfail "alpha*-*-*" "*c30*-*-*" "*c4x*-*-*" \ - "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" "msp430-*-*" \ + "d\[13\]0v*-*-*" "i860-*-*" "mips*-*-*" \ "pdp11-*-*" "xtensa*-*-*" run_dump_test forward } @@ -139,6 +140,7 @@ case $target_triplet in { { mips*-*-* } { } { mn10200-*-* } { } { mn10300-*-* } { } + { msp430*-*-* } { } { pdp11-*-* } { } { tic30*-*-* } { } { tic4x*-*-* } { } @@ -266,8 +268,8 @@ if { ![istarget hppa*-*-*] && ![istarget *c54x*-*-*] } then { # the vax fails because VMS can apparently actually handle this # case in relocs, so gas doesn't handle it itself. - # mn10300 emits two relocs to handle the difference of two symbols. - setup_xfail "mn10300*-*-*" "vax*-*-vms*" + # msp430 and mn10300 emit two relocs to handle the difference of two symbols. + setup_xfail "mn10300*-*-*" "msp430*-*-*" "vax*-*-vms*" do_930509a } diff --git a/gas/testsuite/gas/all/sleb128-4.d b/gas/testsuite/gas/all/sleb128-4.d index 0c56696..89b9565 100644 --- a/gas/testsuite/gas/all/sleb128-4.d +++ b/gas/testsuite/gas/all/sleb128-4.d @@ -1,5 +1,6 @@ #objdump : -s -j .data -j "\$DATA\$" #name : .sleb128 tests (4) +#skip: msp430*-*-* .*: .* diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp index 9ffd257..4196fd7 100644 --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@ -1,5 +1,4 @@ -# Copyright 2012 -# Free Software Foundation, Inc. +# Copyright 2012-2013 Free Software Foundation, Inc. # 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 @@ -62,6 +61,9 @@ if { [is_elf_format] } then { if {[istarget m32r*-*-*]} then { set target_machine -m32r } + if {[istarget "msp430-*-*"]} then { + set target_machine -msp430 + } if {[istarget "score-*-*"]} then { set target_machine -score } @@ -91,6 +93,7 @@ if { [is_elf_format] } then { # function prologues. if {![istarget "mn10300-*-*"] && ![istarget "xtensa*-*-*"] + && ![istarget "msp430*-*-*"] && ![istarget "am3*-*-*"]} then { run_dump_test "ehopt0" } @@ -136,6 +139,7 @@ if { [is_elf_format] } then { { mips*-*-* } { } { mn10200-*-* } { } { mn10300-*-* } { } + { msp43*-*-* } { } { *c54x*-*-* } { } { rx-*-* } { } default { diff --git a/gas/testsuite/gas/lns/lns-big-delta.d b/gas/testsuite/gas/lns/lns-big-delta.d index b6a113e..f4bdcf4 100644 --- a/gas/testsuite/gas/lns/lns-big-delta.d +++ b/gas/testsuite/gas/lns/lns-big-delta.d @@ -10,8 +10,8 @@ Raw dump of debug contents of section \.debug_line: Advance PC by fixed size amount 0 to 0x0 Copy Advance Line by 1 to 3 - Extended opcode 2: set Address to 0x..... + Extended opcode 2: set Address to 0x.* Copy - Advance PC by fixed size amount . to 0x..... + Advance PC by fixed size amount . to 0x.* Extended opcode 1: End of Sequence #pass diff --git a/gas/testsuite/gas/lns/lns.exp b/gas/testsuite/gas/lns/lns.exp index 0febe0b..f1d7f98 100644 --- a/gas/testsuite/gas/lns/lns.exp +++ b/gas/testsuite/gas/lns/lns.exp @@ -38,6 +38,7 @@ if { || [istarget am3*-*-*] || [istarget cr16-*-*] || [istarget crx-*-*] + || [istarget msp430-*-*] || [istarget mn10*-*-*] } { run_dump_test "lns-common-1-alt" run_dump_test "lns-big-delta" diff --git a/gas/testsuite/gas/msp430/msp430.exp b/gas/testsuite/gas/msp430/msp430.exp index 27df900..656ace8 100644 --- a/gas/testsuite/gas/msp430/msp430.exp +++ b/gas/testsuite/gas/msp430/msp430.exp @@ -1,5 +1,4 @@ -# Copyright 2012 -# Free Software Foundation, Inc. +# Copyright 2012-2013 Free Software Foundation, Inc. # 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 @@ -21,4 +20,5 @@ if [expr [istarget "msp430-*-*"]] then { run_dump_test "opcode" + run_dump_test "msp430x" } diff --git a/gas/testsuite/gas/msp430/opcode.d b/gas/testsuite/gas/msp430/opcode.d index 22df51c..9212d89 100644 --- a/gas/testsuite/gas/msp430/opcode.d +++ b/gas/testsuite/gas/msp430/opcode.d @@ -22,14 +22,14 @@ Disassembly of section .text: 0+024 <[^>]*> 8c 10 swpb r12 ; 0+026 <[^>]*> 0d 10 rrc r13 ; 0+028 <[^>]*> 30 41 ret -0+02a <[^>]*> 31 40 00 00 mov #0, r1 ;#0x0000 -0+02e <[^>]*> b0 12 00 00 call #0 ;#0x0000 +0+02a <[^>]*> 31 40 00 00 mov #0, r1 ; +0+02e <[^>]*> b0 12 00 00 call #0 ; 0+032 <[^>]*> 1e 42 00 00 mov &0x0000,r14 ;0x0000 0+036 <[^>]*> 0f 4e mov r14, r15 ; 0+038 <[^>]*> 0f 5f rla r15 ; 0+03a <[^>]*> 0f 7f subc r15, r15 ; 0+03c <[^>]*> 3f e3 inv r15 ; -0+03e <[^>]*> b0 12 00 00 call #0 ;#0x0000 +0+03e <[^>]*> b0 12 00 00 call #0 ; 0+042 <[^>]*> 82 4e 00 00 mov r14, &0x0000 ; 0+046 <[^>]*> 82 4f 00 00 mov r15, &0x0000 ; 0+04a <[^>]*> 1e 42 00 00 mov &0x0000,r14 ;0x0000 @@ -37,7 +37,7 @@ Disassembly of section .text: 0+050 <[^>]*> 0f 5f rla r15 ; 0+052 <[^>]*> 0f 7f subc r15, r15 ; 0+054 <[^>]*> 3f e3 inv r15 ; -0+056 <[^>]*> b0 12 00 00 call #0 ;#0x0000 +0+056 <[^>]*> b0 12 00 00 call #0 ; 0+05a <[^>]*> 82 4e 00 00 mov r14, &0x0000 ; 0+05e <[^>]*> 82 4f 00 00 mov r15, &0x0000 ; 0+062 <[^>]*> 3f 40 f0 00 mov #240, r15 ;#0x00f0 diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index c7265920..1eb581b 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,9 @@ +2013-05-02 Nick Clifton + + * msp430.h: Add MSP430X relocs. + Add some more MSP430 machine numbers. + Add values used by .MSP430.attributes section. + 2013-03-21 Michael Schewe * h8.h: Add new reloc R_H8_DISP32A16 for relaxation of diff --git a/include/elf/msp430.h b/include/elf/msp430.h index 44f5c51..ac8e28c 100644 --- a/include/elf/msp430.h +++ b/include/elf/msp430.h @@ -1,5 +1,5 @@ /* MSP430 ELF support for BFD. - Copyright (C) 2002, 2003, 2004, 2010 Free Software Foundation, Inc. + Copyright (C) 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky This file is part of BFD, the Binary File Descriptor library. @@ -33,6 +33,11 @@ #define E_MSP430_MACH_MSP430x14 14 #define E_MSP430_MACH_MSP430x15 15 #define E_MSP430_MACH_MSP430x16 16 +#define E_MSP430_MACH_MSP430x20 20 +#define E_MSP430_MACH_MSP430x22 22 +#define E_MSP430_MACH_MSP430x23 23 +#define E_MSP430_MACH_MSP430x24 24 +#define E_MSP430_MACH_MSP430x26 26 #define E_MSP430_MACH_MSP430x31 31 #define E_MSP430_MACH_MSP430x32 32 #define E_MSP430_MACH_MSP430x33 33 @@ -40,6 +45,19 @@ #define E_MSP430_MACH_MSP430x42 42 #define E_MSP430_MACH_MSP430x43 43 #define E_MSP430_MACH_MSP430x44 44 +#define E_MSP430_MACH_MSP430X 45 +#define E_MSP430_MACH_MSP430x46 46 +#define E_MSP430_MACH_MSP430x47 47 +#define E_MSP430_MACH_MSP430x54 54 + +#define SHT_MSP430_ATTRIBUTES 0x70000003 /* Section holds ABI attributes. */ +#define SHT_MSP430_SEC_FLAGS 0x7f000005 /* Holds TI compiler's section flags. */ +#define SHT_MSP430_SYM_ALIASES 0x7f000006 /* Holds TI compiler's symbol aliases. */ + +/* Tag values for an attribute section. */ +#define OFBA_MSPABI_Tag_ISA 4 +#define OFBA_MSPABI_Tag_Code_Model 6 +#define OFBA_MSPABI_Tag_Data_Model 8 /* Relocations. */ START_RELOC_NUMBERS (elf_msp430_reloc_type) @@ -52,7 +70,32 @@ START_RELOC_NUMBERS (elf_msp430_reloc_type) RELOC_NUMBER (R_MSP430_16_PCREL_BYTE, 6) RELOC_NUMBER (R_MSP430_2X_PCREL, 7) RELOC_NUMBER (R_MSP430_RL_PCREL, 8) - + RELOC_NUMBER (R_MSP430_8, 9) + RELOC_NUMBER (R_MSP430_SYM_DIFF, 10) END_RELOC_NUMBERS (R_MSP430_max) +START_RELOC_NUMBERS (elf_msp430x_reloc_type) + RELOC_NUMBER (R_MSP430_ABS32, 1) /* aka R_MSP430_32 */ + RELOC_NUMBER (R_MSP430_ABS16, 2) /* aka R_MSP430_16 */ + RELOC_NUMBER (R_MSP430_ABS8, 3) + RELOC_NUMBER (R_MSP430_PCR16, 4) /* aka R_MSP430_16_PCREL */ + RELOC_NUMBER (R_MSP430X_PCR20_EXT_SRC, 5) + RELOC_NUMBER (R_MSP430X_PCR20_EXT_DST, 6) + RELOC_NUMBER (R_MSP430X_PCR20_EXT_ODST, 7) + RELOC_NUMBER (R_MSP430X_ABS20_EXT_SRC, 8) + RELOC_NUMBER (R_MSP430X_ABS20_EXT_DST, 9) + RELOC_NUMBER (R_MSP430X_ABS20_EXT_ODST, 10) + RELOC_NUMBER (R_MSP430X_ABS20_ADR_SRC, 11) + RELOC_NUMBER (R_MSP430X_ABS20_ADR_DST, 12) + RELOC_NUMBER (R_MSP430X_PCR16, 13) /* Like R_MSP430_PCR16 but with overflow checking. */ + RELOC_NUMBER (R_MSP430X_PCR20_CALL, 14) + RELOC_NUMBER (R_MSP430X_ABS16, 15) /* Like R_MSP430_ABS16 but with overflow checking. */ + RELOC_NUMBER (R_MSP430_ABS_HI16, 16) + RELOC_NUMBER (R_MSP430_PREL31, 17) + RELOC_NUMBER (R_MSP430_EHTYPE, 18) /* Mentioned in ABI. */ + RELOC_NUMBER (R_MSP430X_10_PCREL, 19) /* Red Hat invention. Used for Jump instructions. */ + RELOC_NUMBER (R_MSP430X_2X_PCREL, 20) /* Red Hat invention. Used for relaxing jumps. */ + RELOC_NUMBER (R_MSP430X_SYM_DIFF, 21) /* Red Hat invention. Used for relaxing debug info. */ +END_RELOC_NUMBERS (R_MSP430x_max) + #endif /* _ELF_MSP430_H */ diff --git a/include/opcode/ChangeLog b/include/opcode/ChangeLog index 9eed104..2b76a36 100644 --- a/include/opcode/ChangeLog +++ b/include/opcode/ChangeLog @@ -1,3 +1,7 @@ +2013-05-02 Nick Clifton + + * msp430.h: Add patterns for MSP430X instructions. + 2013-04-06 David S. Miller * sparc.h (F_PREFERRED): Define. diff --git a/include/opcode/msp430.h b/include/opcode/msp430.h index d3bf130..caddc42 100644 --- a/include/opcode/msp430.h +++ b/include/opcode/msp430.h @@ -1,6 +1,6 @@ /* Opcode table for the TI MSP430 microcontrollers - Copyright 2002, 2004, 2010 Free Software Foundation, Inc. + Copyright 2002-2013 Free Software Foundation, Inc. Contributed by Dmitry Diky This program is free software; you can redistribute it and/or modify @@ -119,6 +119,74 @@ static struct msp430_opcode_s msp430_opcodes[] = MSP_INSN (bleu, 5, 2, 0, 0xffff), MSP_INSN (ble, 5, 3, 0, 0xffff), + /* MSP430X instructions - these ones use an extension word. + A negative format indicates an MSP430X instruction. */ + MSP_INSN (addcx, -2, 2, 0x6000, 0xf000), + MSP_INSN (addx, -2, 2, 0x5000, 0xf000), + MSP_INSN (andx, -2, 2, 0xf000, 0xf000), + MSP_INSN (bicx, -2, 2, 0xc000, 0xf000), + MSP_INSN (bisx, -2, 2, 0xd000, 0xf000), + MSP_INSN (bitx, -2, 2, 0xb000, 0xf000), + MSP_INSN (cmpx, -2, 2, 0x9000, 0xf000), + MSP_INSN (daddx, -2, 2, 0xa000, 0xf000), + MSP_INSN (movx, -2, 2, 0x4000, 0xf000), + MSP_INSN (subcx, -2, 2, 0x7000, 0xf000), + MSP_INSN (subx, -2, 2, 0x8000, 0xf000), + MSP_INSN (xorx, -2, 2, 0xe000, 0xf000), + + /* MSP430X Synthetic instructions. */ + MSP_INSN (adcx, -1, 1, 0x6300, 0xff30), + MSP_INSN (clra, -1, 1, 0x4300, 0xff30), + MSP_INSN (clrx, -1, 1, 0x4300, 0xff30), + MSP_INSN (dadcx, -1, 1, 0xa300, 0xff30), + MSP_INSN (decx, -1, 1, 0x8310, 0xff30), + MSP_INSN (decda, -1, 1, 0x8320, 0xff30), + MSP_INSN (decdx, -1, 1, 0x8320, 0xff30), + MSP_INSN (incx, -1, 1, 0x5310, 0xff30), + MSP_INSN (incda, -1, 1, 0x5320, 0xff30), + MSP_INSN (incdx, -1, 1, 0x5320, 0xff30), + MSP_INSN (invx, -1, 1, 0xe330, 0xfff0), + MSP_INSN (popx, -1, 1, 0x4130, 0xff30), + MSP_INSN (rlax, -1, 2, 0x5000, 0xf000), + MSP_INSN (rlcx, -1, 2, 0x6000, 0xf000), + MSP_INSN (sbcx, -1, 1, 0x7300, 0xff30), + MSP_INSN (tsta, -1, 1, 0x9300, 0xff30), + MSP_INSN (tstx, -1, 1, 0x9300, 0xff30), + + MSP_INSN (pushx, -3, 1, 0x1200, 0xff80), + MSP_INSN (rrax, -3, 1, 0x1100, 0xff80), + MSP_INSN (rrcx, -3, 1, 0x1000, 0xff80), + MSP_INSN (swpbx, -3, 1, 0x1080, 0xffc0), + MSP_INSN (sxtx, -3, 1, 0x1180, 0xffc0), + + /* MSP430X Address instructions - no extension word needed. + The insn_opnumb field is used to encode the nature of the + instruction for assembly and disassembly purposes. */ + MSP_INSN (calla, -1, 4, 0x1300, 0xff00), + + MSP_INSN (popm, -1, 5, 0x1600, 0xfe00), + MSP_INSN (pushm, -1, 5, 0x1400, 0xfe00), + + MSP_INSN (rrcm, -1, 6, 0x0040, 0xf3e0), + MSP_INSN (rram, -1, 6, 0x0140, 0xf3e0), + MSP_INSN (rlam, -1, 6, 0x0240, 0xf3e0), + MSP_INSN (rrum, -1, 6, 0x0340, 0xf3e0), + + MSP_INSN (rrux, -1, 7, 0x0340, 0xffe0), /* Synthesized in terms of RRUM. */ + + MSP_INSN (adda, -1, 8, 0x00a0, 0xf0b0), + MSP_INSN (cmpa, -1, 8, 0x0090, 0xf0b0), + MSP_INSN (suba, -1, 8, 0x00b0, 0xf0b0), + + MSP_INSN (reta, -1, 9, 0x0110, 0xffff), + MSP_INSN (bra, -1, 9, 0x0000, 0xf0cf), + MSP_INSN (mova, -1, 9, 0x0000, 0xf080), + MSP_INSN (mova, -1, 9, 0x0080, 0xf0b0), + MSP_INSN (mova, -1, 9, 0x00c0, 0xf0f0), + + /* Pseudo instruction to set the repeat field in the extension word. */ + MSP_INSN (rpt, -1, 10, 0x0000, 0x0000), + /* End of instruction set. */ { NULL, 0, 0, 0, 0 } }; diff --git a/ld/ChangeLog b/ld/ChangeLog index 62dd025..5bb24f9 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,19 @@ +2013-05-02 Nick Clifton + + * Makefile.am: Add emsp430X.c + * Makefine.in: Regenerate. + * configure.tgt (msp430): Add msp430X emulation. + * ldmain.c (multiple_definition): Only disable relaxation if it + was enabled by the user. + * ldmain.h (RELAXATION_ENABLED_BY_USER): New macro. + * emulparams/msp430all.sh: Add support for MSP430X. + * emultempl/generic.em: (before_parse): Enable relaxation for the + MSP430. + * scripttempl/msp430.sc: Reorganize sections. Add .rodata + section. + * scripttempl/msp430_3.sc: Likewise. + * NEWS: Mention support for MSP430X. + 2013-05-01 Maciej W. Rozycki * configure.tgt: Replace alpha*-*-linuxecoff* pattern with diff --git a/ld/Makefile.am b/ld/Makefile.am index e0366f3..0eba8a9 100644 --- a/ld/Makefile.am +++ b/ld/Makefile.am @@ -424,6 +424,7 @@ ALL_EMULATION_SOURCES = \ emsp430xW423.c \ emsp430xW425.c \ emsp430xW427.c \ + emsp430X.c \ enews.c \ ens32knbsd.c \ eor32.c \ @@ -1773,6 +1774,10 @@ emsp430xW427.c: $(srcdir)/emulparams/msp430all.sh \ $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ ${GEN_DEPENDS} ${GENSCRIPTS} msp430xW427 "$(tdir_msp430xW427)" msp430all +emsp430X.c: $(srcdir)/emulparams/msp430all.sh \ + $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ + ${GEN_DEPENDS} + ${GENSCRIPTS} msp430X "$(tdir_msp430X)" msp430all enews.c: $(srcdir)/emulparams/news.sh \ $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} ${GENSCRIPTS} news "$(tdir_news)" diff --git a/ld/Makefile.in b/ld/Makefile.in index 042e9f2..9140c73 100644 --- a/ld/Makefile.in +++ b/ld/Makefile.in @@ -732,6 +732,7 @@ ALL_EMULATION_SOURCES = \ emsp430xW423.c \ emsp430xW425.c \ emsp430xW427.c \ + emsp430X.c \ enews.c \ ens32knbsd.c \ eor32.c \ @@ -1343,6 +1344,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emmo.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emn10200.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emn10300.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430X.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430x110.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430x1101.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emsp430x1111.Po@am__quote@ @@ -3259,6 +3261,10 @@ emsp430xW427.c: $(srcdir)/emulparams/msp430all.sh \ $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ ${GEN_DEPENDS} ${GENSCRIPTS} msp430xW427 "$(tdir_msp430xW427)" msp430all +emsp430X.c: $(srcdir)/emulparams/msp430all.sh \ + $(ELF_GEN_DEPS) $(srcdir)/scripttempl/elf32msp430.sc \ + ${GEN_DEPENDS} + ${GENSCRIPTS} msp430X "$(tdir_msp430X)" msp430all enews.c: $(srcdir)/emulparams/news.sh \ $(srcdir)/emultempl/generic.em $(srcdir)/scripttempl/aout.sc ${GEN_DEPENDS} ${GENSCRIPTS} news "$(tdir_news)" diff --git a/ld/NEWS b/ld/NEWS index 5217e4c..fa11d2d 100644 --- a/ld/NEWS +++ b/ld/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* Add support for the Texas Instruments MSP430X processor. + * Add support for Altera Nios II. * Add support for the V850E3V5 architecture. diff --git a/ld/configure.tgt b/ld/configure.tgt index 8d7aec4..5694580 100644 --- a/ld/configure.tgt +++ b/ld/configure.tgt @@ -519,7 +519,7 @@ mn10300-*-*) targ_emul=mn10300 mt-*elf) targ_emul=elf32mt ;; msp430-*-*) targ_emul=msp430x110 - targ_extra_emuls="msp430x112 msp430x1101 msp430x1111 msp430x1121 msp430x1122 msp430x1132 msp430x122 msp430x123 msp430x1222 msp430x1232 msp430x133 msp430x135 msp430x1331 msp430x1351 msp430x147 msp430x148 msp430x149 msp430x155 msp430x156 msp430x157 msp430x167 msp430x168 msp430x169 msp430x1610 msp430x1611 msp430x1612 msp430x2101 msp430x2111 msp430x2121 msp430x2131 msp430x311 msp430x312 msp430x313 msp430x314 msp430x315 msp430x323 msp430x325 msp430x336 msp430x337 msp430x412 msp430x413 msp430x415 msp430x417 msp430xE423 msp430xE425 msp430xE427 msp430xW423 msp430xW425 msp430xW427 msp430xG437 msp430xG438 msp430xG439 msp430x435 msp430x436 msp430x437 msp430x447 msp430x448 msp430x449" + targ_extra_emuls="msp430x112 msp430x1101 msp430x1111 msp430x1121 msp430x1122 msp430x1132 msp430x122 msp430x123 msp430x1222 msp430x1232 msp430x133 msp430x135 msp430x1331 msp430x1351 msp430x147 msp430x148 msp430x149 msp430x155 msp430x156 msp430x157 msp430x167 msp430x168 msp430x169 msp430x1610 msp430x1611 msp430x1612 msp430x2101 msp430x2111 msp430x2121 msp430x2131 msp430x311 msp430x312 msp430x313 msp430x314 msp430x315 msp430x323 msp430x325 msp430x336 msp430x337 msp430x412 msp430x413 msp430x415 msp430x417 msp430xE423 msp430xE425 msp430xE427 msp430xW423 msp430xW425 msp430xW427 msp430xG437 msp430xG438 msp430xG439 msp430x435 msp430x436 msp430x437 msp430x447 msp430x448 msp430x449 msp430X" ;; nios2*-*-*) targ_emul=nios2elf ;; ns32k-pc532-mach* | ns32k-pc532-ux*) targ_emul=pc532macha ;; diff --git a/ld/emulparams/msp430all.sh b/ld/emulparams/msp430all.sh index 57d21c2..3a2f79d 100644 --- a/ld/emulparams/msp430all.sh +++ b/ld/emulparams/msp430all.sh @@ -551,3 +551,12 @@ RAM_START=0x0200 RAM_SIZE=0x400 STACK=0x600 fi + +if [ "${MSP430_NAME}" = "msp430X" ] ; then +ARCH=msp:43 +ROM_START=0x02000 +ROM_SIZE=0x0dfe0 +RAM_START=0x10000 +RAM_SIZE=0x30000 +STACK=0x600 +fi diff --git a/ld/emultempl/generic.em b/ld/emultempl/generic.em index 20ec356..dce2bff 100644 --- a/ld/emultempl/generic.em +++ b/ld/emultempl/generic.em @@ -57,6 +57,18 @@ gld${EMULATION_NAME}_before_parse (void) #ifndef TARGET_ /* I.e., if not generic. */ ldfile_set_output_arch ("`echo ${ARCH}`", bfd_arch_unknown); #endif /* not TARGET_ */ +EOF + # The MSP430 port *needs* linker relaxtion in order to cope with large + # functions where conditional branches do not fit into a +/- 1024 byte range. + case ${target} in + msp430-*-* ) +fragment < 0) + (link_info.disable_target_specific_optimizations > 1) #define RELAXATION_ENABLED \ + (link_info.disable_target_specific_optimizations == 0 \ + || link_info.disable_target_specific_optimizations == 1) +#define RELAXATION_ENABLED_BY_USER \ (link_info.disable_target_specific_optimizations == 0) -#define DISABLE_RELAXATION \ +#define TARGET_ENABLE_RELAXATION \ do { link_info.disable_target_specific_optimizations = 1; } while (0) +#define DISABLE_RELAXATION \ + do { link_info.disable_target_specific_optimizations = 2; } while (0) #define ENABLE_RELAXATION \ do { link_info.disable_target_specific_optimizations = 0; } while (0) diff --git a/ld/scripttempl/elf32msp430.sc b/ld/scripttempl/elf32msp430.sc index 53a87b8..c699a91 100644 --- a/ld/scripttempl/elf32msp430.sc +++ b/ld/scripttempl/elf32msp430.sc @@ -35,6 +35,31 @@ MEMORY SECTIONS { + /* Bootloader. */ + .bootloader ${RELOCATING-0} : + { + ${RELOCATING+ PROVIDE (__boot_start = .) ; } + *(.bootloader) + ${RELOCATING+. = ALIGN(2);} + *(.bootloader.*) + } ${RELOCATING+ > bootloader} + + /* Information memory. */ + .infomem ${RELOCATING-0} : + { + *(.infomem) + ${RELOCATING+. = ALIGN(2);} + *(.infomem.*) + } ${RELOCATING+ > infomem} + + /* Information memory (not loaded into MPU). */ + .infomemnobits ${RELOCATING-0} : + { + *(.infomemnobits) + ${RELOCATING+. = ALIGN(2);} + *(.infomemnobits.*) + } ${RELOCATING+ > infomemnobits} + /* Read-only sections, merged into text segment. */ ${TEXT_DYNAMIC+${DYNAMIC}} .hash ${RELOCATING-0} : { *(.hash) } @@ -122,6 +147,8 @@ SECTIONS *(.text) ${RELOCATING+. = ALIGN(2);} *(.text.*) + ${RELOCATING+. = ALIGN(2);} + *(.text:*) ${RELOCATING+. = ALIGN(2);} *(SORT_NONE(.fini9)) @@ -139,42 +166,84 @@ SECTIONS _etext = .; } ${RELOCATING+ > text} - .data ${RELOCATING-0} : ${RELOCATING+AT (ADDR (.text) + SIZEOF (.text))} + .rodata : + { + . = ALIGN(2); + *(.plt) + *(.rodata .rodata.* .gnu.linkonce.r.* .const .const:*) + *(.rodata1) + + *(.eh_frame_hdr) + KEEP (*(.eh_frame)) + + KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) + + PROVIDE (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE (__preinit_array_end = .); + + PROVIDE (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE (__init_array_end = .); + + PROVIDE (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE (__fini_array_end = .); + LONG(0); /* Sentinel. */ + + /* gcc uses crtbegin.o to find the start of the constructors, so + we make sure it is first. Because this is a wildcard, it + doesn't matter if the user does not actually link against + crtbegin.o; the linker won't look for a file to match a + wildcard. The wildcard also means that it doesn't matter which + directory crtbegin.o is in. */ + KEEP (*crtbegin*.o(.ctors)) + + /* We don't want to include the .ctor section from from the + crtend.o file until after the sorted ctors. The .ctor section + from the crtend file contains the end of ctors marker and it + must be last */ + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + + KEEP (*crtbegin*.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + } ${RELOCATING+ > text} + + .vectors ${RELOCATING-0}: + { + ${RELOCATING+ PROVIDE (__vectors_start = .) ; } + *(.vectors*) + ${RELOCATING+ _vectors_end = . ; } + } ${RELOCATING+ > vectors} + + .data ${RELOCATING-0} : ${RELOCATING+AT (ADDR (.text) + SIZEOF (.text) + SIZEOF (.rodata))} { ${RELOCATING+ PROVIDE (__data_start = .) ; } + ${RELOCATING+ PROVIDE (__datastart = .) ; } ${RELOCATING+. = ALIGN(2);} + + KEEP (*(.jcr)) + *(.data.rel.ro.local) *(.data.rel.ro*) + *(.dynamic) + *(.data) *(.data.*) *(.gnu.linkonce.d*) + KEEP (*(.gnu.linkonce.d.*personality*)) + *(.data1) + *(.got.plt) *(.got) + ${RELOCATING+. = ALIGN(2);} + *(.sdata .sdata.* .gnu.linkonce.s.*) ${RELOCATING+. = ALIGN(2);} ${RELOCATING+ _edata = . ; } } ${RELOCATING+ > data} - /* Bootloader. */ - .bootloader ${RELOCATING-0} : - { - ${RELOCATING+ PROVIDE (__boot_start = .) ; } - *(.bootloader) - ${RELOCATING+. = ALIGN(2);} - *(.bootloader.*) - } ${RELOCATING+ > bootloader} - - /* Information memory. */ - .infomem ${RELOCATING-0} : - { - *(.infomem) - ${RELOCATING+. = ALIGN(2);} - *(.infomem.*) - } ${RELOCATING+ > infomem} - - /* Information memory (not loaded into MPU). */ - .infomemnobits ${RELOCATING-0} : - { - *(.infomemnobits) - ${RELOCATING+. = ALIGN(2);} - *(.infomemnobits.*) - } ${RELOCATING+ > infomemnobits} - .bss ${RELOCATING+ SIZEOF(.data) + ADDR(.data)} : { ${RELOCATING+. = ALIGN(2);} @@ -194,13 +263,6 @@ SECTIONS ${RELOCATING+ _end = . ; } } ${RELOCATING+ > data} - .vectors ${RELOCATING-0}: - { - ${RELOCATING+ PROVIDE (__vectors_start = .) ; } - *(.vectors*) - ${RELOCATING+ _vectors_end = . ; } - } ${RELOCATING+ > vectors} - ${HEAP_SECTION_MSP430} /* Stabs for profiling information*/ @@ -214,12 +276,18 @@ SECTIONS .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } - EOF -. $srcdir/scripttempl/DWARF.sc +source $srcdir/scripttempl/DWARF.sc cat < text} + .rodata : + { + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.const) + *(.const:*) + } ${RELOCATING+ > text} + .data ${RELOCATING-0} : ${RELOCATING+AT (ADDR (.text) + SIZEOF (.text))} { ${RELOCATING+ PROVIDE (__data_start = .) ; } @@ -152,6 +161,13 @@ SECTIONS ${RELOCATING+ _vectors_end = . ; } } ${RELOCATING+ > vectors} + .MP430.attributes 0 : + { + KEEP (*(.MSP430.attributes)) + KEEP (*(.gnu.attributes)) + KEEP (*(__TI_build_attributes)) + } + /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index 664b33e..7b2e506 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2013-05-02 Nick Clifton + + * ld-elf/flags1.d: Expect this test to pass on the MSP430. + * ld-elf/init-fini-arrays.d: Expect this test to fail on the + MSP430. + * ld-elf/merge.d: Expect this test to pass on the MSP430. + * ld-elf/sec64k.exp: Skip these tests for the MSP430. + * ld-gc/pr13683.d: Expect this test to fail on the MSP430. + * ld-srec/srec.exp: Expect these tests to fail on the MSP430. + * ld-undefined/undefined.exp: Expect the UNDEFINED LINE test to + fail on the MSP430. + 2013-05-01 Maciej W. Rozycki * lib/ld-lib.exp (check_shared_lib_support): Also exclude diff --git a/ld/testsuite/ld-elf/flags1.d b/ld/testsuite/ld-elf/flags1.d index ab8facc..63c2e3a 100644 --- a/ld/testsuite/ld-elf/flags1.d +++ b/ld/testsuite/ld-elf/flags1.d @@ -3,9 +3,9 @@ #objcopy_linked_file: --set-section-flags .post_text_reserve=contents,alloc,load,readonly,code #readelf: -l --wide #xfail: "avr-*-*" "dlx-*-*" "h8300-*-*" "i960-*-*" "ip2k-*-*" "m32r-*-*" -#xfail: "moxie-*-*" "mt-*-*" "msp430-*-*" "*-*-nacl*" +#xfail: "moxie-*-*" "mt-*-*" "*-*-nacl*" #xfail: "*-*-hpux*" "hppa*64*-*-*" -# Fails on the AVR, DLX, H8300, I960, IP2K, M32R, MOXIE, MT, and MSP430, +# Fails on the AVR, DLX, H8300, I960, IP2K, M32R, MOXIE, MT, # and all NaCl targets, # because the two sections are not merged into one segment. # (There is no good reason why they have to be). diff --git a/ld/testsuite/ld-elf/init-fini-arrays.d b/ld/testsuite/ld-elf/init-fini-arrays.d index 1b182b9..46b536c 100644 --- a/ld/testsuite/ld-elf/init-fini-arrays.d +++ b/ld/testsuite/ld-elf/init-fini-arrays.d @@ -1,9 +1,10 @@ #source: init-fini-arrays.s #ld: -r #readelf: -S --wide -#xfail: cr16-*-* crx-*-* +#xfail: cr16-*-* crx-*-* msp430-*-* +# msp430 puts the init_array and fini_array inside the .rodata section. # cr16 and crx use non-standard scripts with memory regions, which don't play -# well with unique group sections under ld -r. +# well with unique group sections under ld -r. #... \[[ 0-9]+\] \.init_array\.01000[ \t]+PROGBITS[ \t0-9a-f]+WA?.* diff --git a/ld/testsuite/ld-elf/merge.d b/ld/testsuite/ld-elf/merge.d index 3593f96..c50de10 100644 --- a/ld/testsuite/ld-elf/merge.d +++ b/ld/testsuite/ld-elf/merge.d @@ -4,7 +4,7 @@ #xfail: "arc-*-*" "avr-*-*" "bfin-*-*" "cr16-*-*" "cris*-*-*" "crx-*-*" "d10v-*-*" "d30v-*-*" #xfail: "dlx-*-*" "fr30-*-*" "frv-*-*" "hppa*64*-*-*" "h8300-*-*" "score-*-*" #xfail: "i370-*-*" "i860-*-*" "i960-*-*" "ip2k-*-*" "iq2000-*-*" "lm32-*-*" -#xfail: "mcore-*-*" "mn102*-*-*" "mips*-*-*" "ms1-*-*" "msp430-*-*" "mep-*-*" +#xfail: "mcore-*-*" "mn102*-*-*" "mips*-*-*" "ms1-*-*" "mep-*-*" #xfail: "or32-*-*" "pj-*-*" "sparc*-*-*" "tic6x-*-*" "vax-*-*" "xstormy16-*-*" #xfail: "xtensa*-*-*" "metag-*-*" diff --git a/ld/testsuite/ld-elf/sec64k.exp b/ld/testsuite/ld-elf/sec64k.exp index 7c04c9b..7c9f292 100644 --- a/ld/testsuite/ld-elf/sec64k.exp +++ b/ld/testsuite/ld-elf/sec64k.exp @@ -34,6 +34,7 @@ if { [istarget "arc-*-*"] || [istarget "d30v-*-*"] || [istarget "dlx-*-*"] || [istarget "i960-*-*"] + || [istarget "msp430*-*-*"] || [istarget "or32-*-*"] || [istarget "pj*-*-*"] || [istarget "m32r-*-*"] } { diff --git a/ld/testsuite/ld-gc/pr13683.d b/ld/testsuite/ld-gc/pr13683.d index 19b2598..b38b9d1 100644 --- a/ld/testsuite/ld-gc/pr13683.d +++ b/ld/testsuite/ld-gc/pr13683.d @@ -2,7 +2,7 @@ #source: dummy.s #ld: --gc-sections -e main --defsym foo=foo2 tmpdir/pr13683.o #nm: --format=bsd -#xfail: sh64*-*-* iq2000-*-* lm32-*-* epiphany-*-* mips64vr-*-* frv-*-* m32c-*-* rl78-*-* rx-*-* sh-*-* powerpc*-*-eabivle +#xfail: sh64*-*-* iq2000-*-* lm32-*-* epiphany-*-* mips64vr-*-* frv-*-* m32c-*-* rl78-*-* rx-*-* sh-*-* powerpc*-*-eabivle msp430-*-* # Note - look for both "foo" and "foo2" being defined, non-zero function symbols diff --git a/ld/testsuite/ld-srec/srec.exp b/ld/testsuite/ld-srec/srec.exp index 001fb2e..4a433d1 100644 --- a/ld/testsuite/ld-srec/srec.exp +++ b/ld/testsuite/ld-srec/srec.exp @@ -1,7 +1,6 @@ # Test linking directly to S-records. # By Ian Lance Taylor, Cygnus Support. -# Copyright 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2011, 2012 -# Free Software Foundation, Inc. +# Copyright 1999-2013 Free Software Foundation, Inc. # # This file is part of the GNU Binutils. # @@ -269,6 +268,11 @@ proc run_srec_test { test objs } { set flags "$flags -no-relax" } + # MSP430 targets always relax. + if [istarget msp430*-*-*] { + setup_xfail "msp430*-*-*" + } + # Epiphany needs some help too if [istarget epiphany*-*-*] { set flags "$flags --defsym _start=00000060" diff --git a/ld/testsuite/ld-undefined/undefined.exp b/ld/testsuite/ld-undefined/undefined.exp index 65fbc4b..49af737 100644 --- a/ld/testsuite/ld-undefined/undefined.exp +++ b/ld/testsuite/ld-undefined/undefined.exp @@ -128,6 +128,11 @@ setup_xfail mcore-*-elf setup_xfail mep-*-* setup_xfail mips-sgi-irix6* setup_xfail "sh64-*-*" +# Fails for the MSP430 because it uses SYM_DIFF relocs but it does +# not provide a special_function for handling them. If optimization +# is enabled then this test passes because function()'s prologue is +# eliminated. +setup_xfail "msp430-*-*" # The undefined test fails on 31 bit s/390 because the address of the # function `this_function_is_not_defined' is stored in the literal pool of diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 2912765..88f9294 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,7 @@ +2013-05-02 Nick Clifton + + * msp430-dis.c: Add support for MSP430X instructions. + 2013-04-24 Sandra Loosemore * nios2-opc.c (nios2_builtin_reg): Rename "fstatus" control register diff --git a/opcodes/msp430-dis.c b/opcodes/msp430-dis.c index 71690a3..46da3cc 100644 --- a/opcodes/msp430-dis.c +++ b/opcodes/msp430-dis.c @@ -1,7 +1,6 @@ /* Disassemble MSP430 instructions. - Copyright (C) 2002, 2004, 2005, 2007, 2009, 2010, 2012 - Free Software Foundation, Inc. - + Copyright (C) 2002-2013 Free Software Foundation, Inc. + Contributed by Dmitry Diky This file is part of the GNU opcodes library. @@ -83,26 +82,80 @@ msp430_nooperands (struct msp430_opcode_s *opcode, } static int +print_as2_reg_name (int regno, char * op1, char * comm1, + int c2, int c3, int cd) +{ + switch (regno) + { + case 2: + sprintf (op1, "#4"); + sprintf (comm1, "r2 As==10"); + return c2; + + case 3: + sprintf (op1, "#2"); + sprintf (comm1, "r3 As==10"); + return c3; + + default: + /* Indexed register mode @Rn. */ + sprintf (op1, "@r%d", regno); + return cd; + } +} + +static int +print_as3_reg_name (int regno, char * op1, char * comm1, + int c2, int c3, int cd) +{ + switch (regno) + { + case 2: + sprintf (op1, "#8"); + sprintf (comm1, "r2 As==11"); + return c2; + + case 3: + sprintf (op1, "#-1"); + sprintf (comm1, "r3 As==11"); + return c3; + + default: + /* Post incremented @Rn+. */ + sprintf (op1, "@r%d+", regno); + return cd; + } +} + +static int msp430_singleoperand (disassemble_info *info, struct msp430_opcode_s *opcode, bfd_vma addr, unsigned short insn, char *op, char *comm, + unsigned short extension_word, int *cycles) { int regs = 0, regd = 0; int ad = 0, as = 0; int where = 0; int cmd_len = 2; - short dst = 0; + int dst = 0; + int fmt; + int extended_dst = extension_word & 0xf; regd = insn & 0x0f; regs = (insn & 0x0f00) >> 8; as = (insn & 0x0030) >> 4; ad = (insn & 0x0080) >> 7; - switch (opcode->fmt) + if (opcode->fmt < 0) + fmt = (- opcode->fmt) - 1; + else + fmt = opcode->fmt; + + switch (fmt) { case 0: /* Emulated work with dst register. */ if (regs != 2 && regs != 3 && regs != 1) @@ -146,6 +199,13 @@ msp430_singleoperand (disassemble_info *info, sprintf (op, "0x%04x", dst); sprintf (comm, "PC rel. abs addr 0x%04x", PS ((short) (addr + 2) + dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op, "0x%05x", dst); + sprintf (comm, "PC rel. abs addr 0x%05lx", + (long)((addr + 2 + dst) & 0xfffff)); + } } else if (regd == 2) { @@ -154,12 +214,25 @@ msp430_singleoperand (disassemble_info *info, cmd_len += 2; *cycles = 4; sprintf (op, "&0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op, "&0x%05x", dst & 0xfffff); + } } else { dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; *cycles = 4; + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + } + else if (dst & 0x8000) + dst |= -1 << 16; sprintf (op, "%d(r%d)", dst, regd); } } @@ -183,51 +256,31 @@ msp430_singleoperand (disassemble_info *info, } else if (as == 2) { - *cycles = 1; - if (regd == 2) - { - sprintf (op, "#4"); - sprintf (comm, "r2 As==10"); - } - else if (regd == 3) - { - sprintf (op, "#2"); - sprintf (comm, "r3 As==10"); - } - else - { - *cycles = 3; - /* Indexed register mode @Rn. */ - sprintf (op, "@r%d", regd); - } + * cycles = print_as2_reg_name (regd, op, comm, 1, 1, 3); } else if (as == 3) { - *cycles = 1; - if (regd == 2) - { - sprintf (op, "#8"); - sprintf (comm, "r2 As==11"); - } - else if (regd == 3) - { - sprintf (op, "#-1"); - sprintf (comm, "r3 As==11"); - } - else if (regd == 0) + if (regd == 0) { *cycles = 3; /* absolute. @pc+ */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op, "#%d", dst); - sprintf (comm, "#0x%04x", PS (dst)); + if (dst > 9 || dst < 0) + sprintf (comm, "#0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + sprintf (op, "#%d", dst); + if (dst > 9 || dst < 0) + sprintf (comm, "#0x%05x", dst); + } } else - { - *cycles = 3; - sprintf (op, "@r%d+", regd); - } + * cycles = print_as3_reg_name (regd, op, comm, 1, 1, 3); } else if (as == 1) { @@ -240,6 +293,13 @@ msp430_singleoperand (disassemble_info *info, sprintf (op, "0x%04x", PS (dst)); sprintf (comm, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op, "0x%05x", dst & 0xffff); + sprintf (comm, "PC rel. 0x%05lx", + (long)((addr + 2 + dst) & 0xfffff)); + } } else if (regd == 2) { @@ -247,6 +307,11 @@ msp430_singleoperand (disassemble_info *info, dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op, "&0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op, "&0x%05x", dst & 0xfffff); + } } else if (regd == 3) { @@ -256,10 +321,20 @@ msp430_singleoperand (disassemble_info *info, } else { - /* Indexd. */ + /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + } + else if (dst & 0x8000) + dst |= -1 << 16; sprintf (op, "%d(r%d)", dst, regd); + if (dst > 9 || dst < 0) + sprintf (comm, "%05x", dst); } } break; @@ -273,7 +348,7 @@ msp430_singleoperand (disassemble_info *info, where *= 2; sprintf (op, "$%+-8d", where + 2); - sprintf (comm, "abs 0x%x", PS ((short) (addr) + 2 + where)); + sprintf (comm, "abs 0x%lx", (long) (addr + 2 + where)); *cycles = 2; return 2; break; @@ -293,19 +368,28 @@ msp430_doubleoperand (disassemble_info *info, char *op2, char *comm1, char *comm2, + unsigned short extension_word, int *cycles) { int regs = 0, regd = 0; int ad = 0, as = 0; int cmd_len = 2; - short dst = 0; + int dst = 0; + int fmt; + int extended_dst = extension_word & 0xf; + int extended_src = (extension_word >> 7) & 0xf; regd = insn & 0x0f; regs = (insn & 0x0f00) >> 8; as = (insn & 0x0030) >> 4; ad = (insn & 0x0080) >> 7; - if (opcode->fmt == 0) + if (opcode->fmt < 0) + fmt = (- opcode->fmt) - 1; + else + fmt = opcode->fmt; + + if (fmt == 0) { /* Special case: rla and rlc are the only 2 emulated instructions that fall into two operand instructions. */ @@ -343,7 +427,15 @@ msp430_doubleoperand (disassemble_info *info, sprintf (op1, "0x%04x", PS (dst)); sprintf (comm1, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); - + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + sprintf (op1, "0x%05x", dst & 0xfffff); + sprintf (comm1, "PC rel. 0x%05lx", + (long)((addr + 2 + dst) & 0xfffff)); + } } else if (regd == 2) { @@ -356,19 +448,35 @@ msp430_doubleoperand (disassemble_info *info, cmd_len += 4; *cycles = 6; sprintf (op1, "&0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op1, "&0x%05x", dst & 0xfffff); + } } else { /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + } + else if (dst & 0x8000) + dst |= -1 << 16; cmd_len += 4; *cycles = 6; sprintf (op1, "%d(r%d)", dst, regd); + if (dst > 9 || dst < -9) + sprintf (comm1, "#0x%05x", dst); } } *op2 = 0; *comm2 = 0; + return cmd_len; } @@ -386,7 +494,7 @@ msp430_doubleoperand (disassemble_info *info, *cycles = 1; if (regs == 3) { - /* Constsnts. */ + /* Constants. */ sprintf (op1, "#0"); sprintf (comm1, "r3 As==00"); } @@ -398,56 +506,31 @@ msp430_doubleoperand (disassemble_info *info, } else if (as == 2) { - *cycles = 1; - - if (regs == 2) - { - sprintf (op1, "#4"); - sprintf (comm1, "r2 As==10"); - } - else if (regs == 3) - { - sprintf (op1, "#2"); - sprintf (comm1, "r3 As==10"); - } - else - { - *cycles = 2; - - /* Indexed register mode @Rn. */ - sprintf (op1, "@r%d", regs); - } - if (!regs) - *cycles = 3; + * cycles = print_as2_reg_name (regs, op1, comm1, 1, 1, regs == 0 ? 3 : 2); } else if (as == 3) { - if (regs == 2) - { - sprintf (op1, "#8"); - sprintf (comm1, "r2 As==11"); - *cycles = 1; - } - else if (regs == 3) - { - sprintf (op1, "#-1"); - sprintf (comm1, "r3 As==11"); - *cycles = 1; - } - else if (regs == 0) + if (regs == 0) { *cycles = 3; /* Absolute. @pc+. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; sprintf (op1, "#%d", dst); - sprintf (comm1, "#0x%04x", PS (dst)); + if (dst > 9 || dst < 0) + sprintf (comm1, "#0x%04x", PS (dst)); + if (extended_src) + { + dst |= extended_src << 16; + if (dst & 0x80000) + dst |= -1 << 20; + sprintf (op1, "#%d", dst); + if (dst > 9 || dst < 0) + sprintf (comm1, "0x%05x", dst & 0xfffff); + } } else - { - *cycles = 2; - sprintf (op1, "@r%d+", regs); - } + * cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2); } else if (as == 1) { @@ -460,6 +543,15 @@ msp430_doubleoperand (disassemble_info *info, sprintf (op1, "0x%04x", PS (dst)); sprintf (comm1, "PC rel. 0x%04x", PS ((short) addr + 2 + dst)); + if (extended_src) + { + dst |= extended_src << 16; + if (dst & 0x80000) + dst |= -1 << 20; + sprintf (op1, "0x%05x", dst & 0xfffff); + sprintf (comm1, "PC rel. 0x%05lx", + (long) ((addr + 2 + dst) & 0xfffff)); + } } else if (regs == 2) { @@ -469,6 +561,12 @@ msp430_doubleoperand (disassemble_info *info, cmd_len += 2; sprintf (op1, "&0x%04x", PS (dst)); sprintf (comm1, "0x%04x", PS (dst)); + if (extended_src) + { + dst |= extended_src << 16; + sprintf (op1, "&0x%05x", dst & 0xfffff); + * comm1 = 0; + } } else if (regs == 3) { @@ -482,7 +580,17 @@ msp430_doubleoperand (disassemble_info *info, /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; + if (extended_src) + { + dst |= extended_src << 16; + if (dst & 0x80000) + dst |= -1 << 20; + } + else if (dst & 0x8000) + dst |= -1 << 16; sprintf (op1, "%d(r%d)", dst, regs); + if (dst > 9 || dst < -9) + sprintf (comm1, "0x%05x", dst); } } @@ -517,6 +625,15 @@ msp430_doubleoperand (disassemble_info *info, sprintf (op2, "0x%04x", PS (dst)); sprintf (comm2, "PC rel. 0x%04x", PS ((short) addr + cmd_len + dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + sprintf (op2, "0x%05x", dst & 0xfffff); + sprintf (comm2, "PC rel. 0x%05lx", + (long)((addr + cmd_len + dst) & 0xfffff)); + } cmd_len += 2; } else if (regd == 2) @@ -525,11 +642,28 @@ msp430_doubleoperand (disassemble_info *info, dst = msp430dis_opcode (addr + cmd_len, info); cmd_len += 2; sprintf (op2, "&0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + sprintf (op2, "&0x%05x", dst & 0xfffff); + } } else { dst = msp430dis_opcode (addr + cmd_len, info); cmd_len += 2; + if (dst & 0x8000) + dst |= -1 << 16; + if (dst > 9 || dst < 0) + sprintf (comm2, "0x%04x", PS (dst)); + if (extended_dst) + { + dst |= extended_dst << 16; + if (dst & 0x80000) + dst |= -1 << 20; + if (dst > 9 || dst < 0) + sprintf (comm2, "0x%05x", dst & 0xfffff); + } sprintf (op2, "%d(r%d)", dst, regd); } } @@ -577,40 +711,11 @@ msp430_branchinstr (disassemble_info *info, } else if (as == 2) { - if (regs == 2) - { - *cycles = 2; - sprintf (op1, "#4"); - sprintf (comm1, "r2 As==10"); - } - else if (regs == 3) - { - *cycles = 1; - sprintf (op1, "#2"); - sprintf (comm1, "r3 As==10"); - } - else - { - /* Indexed register mode @Rn. */ - *cycles = 2; - sprintf (op1, "@r%d", regs); - } + * cycles = print_as2_reg_name (regs, op1, comm1, 2, 1, 2); } else if (as == 3) { - if (regs == 2) - { - *cycles = 1; - sprintf (op1, "#8"); - sprintf (comm1, "r2 As==11"); - } - else if (regs == 3) - { - *cycles = 1; - sprintf (op1, "#-1"); - sprintf (comm1, "r3 As==11"); - } - else if (regs == 0) + if (regs == 0) { /* Absolute. @pc+ */ *cycles = 3; @@ -619,10 +724,7 @@ msp430_branchinstr (disassemble_info *info, sprintf (op1, "#0x%04x", PS (dst)); } else - { - *cycles = 2; - sprintf (op1, "@r%d+", regs); - } + * cycles = print_as3_reg_name (regs, op1, comm1, 1, 1, 2); } else if (as == 1) { @@ -653,9 +755,11 @@ msp430_branchinstr (disassemble_info *info, } else { - /* Indexd. */ + /* Indexed. */ dst = msp430dis_opcode (addr + 2, info); cmd_len += 2; + if (dst & 0x8000) + dst |= -1 << 16; sprintf (op1, "%d(r%d)", dst, regs); } } @@ -663,6 +767,82 @@ msp430_branchinstr (disassemble_info *info, return cmd_len; } +static int +msp430x_calla_instr (disassemble_info * info, + bfd_vma addr, + unsigned short insn, + char * op1, + char * comm1, + int * cycles) +{ + unsigned int ureg = insn & 0xf; + int reg = insn & 0xf; + int am = (insn & 0xf0) >> 4; + int cmd_len = 2; + unsigned short udst = 0; + short dst = 0; + + switch (am) + { + case 4: /* CALLA Rdst */ + *cycles = 1; + sprintf (op1, "r%d", reg); + break; + + case 5: /* CALLA x(Rdst) */ + *cycles = 3; + dst = msp430dis_opcode (addr + 2, info); + cmd_len += 2; + sprintf (op1, "%d(r%d)", dst, reg); + if (reg == 0) + sprintf (comm1, "PC rel. 0x%05lx", (long) (addr + 2 + dst)); + else + sprintf (comm1, "0x%05x", dst); + break; + + case 6: /* CALLA @Rdst */ + *cycles = 2; + sprintf (op1, "@r%d", reg); + break; + + case 7: /* CALLA @Rdst+ */ + *cycles = 2; + sprintf (op1, "@r%d+", reg); + break; + + case 8: /* CALLA &abs20 */ + udst = msp430dis_opcode (addr + 2, info); + cmd_len += 2; + *cycles = 4; + sprintf (op1, "&%d", (ureg << 16) + udst); + sprintf (comm1, "0x%05x", (ureg << 16) + udst); + break; + + case 9: /* CALLA pcrel-sym */ + dst = msp430dis_opcode (addr + 2, info); + cmd_len += 2; + *cycles = 4; + sprintf (op1, "%d(PC)", (reg << 16) + dst); + sprintf (comm1, "PC rel. 0x%05lx", + (long) (addr + 2 + dst + (reg << 16))); + break; + + case 11: /* CALLA #imm20 */ + udst = msp430dis_opcode (addr + 2, info); + cmd_len += 2; + *cycles = 4; + sprintf (op1, "#%d", (ureg << 16) + udst); + sprintf (comm1, "0x%05x", (ureg << 16) + udst); + break; + + default: + strcpy (comm1, _("unercognised CALLA addressing mode")); + return -1; + } + + return cmd_len; +} + int print_insn_msp430 (bfd_vma addr, disassemble_info *info) { @@ -674,10 +854,14 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) unsigned short insn; int cycles = 0; char *bc = ""; - char dinfo[32]; /* Debug purposes. */ + unsigned short extension_word = 0; insn = msp430dis_opcode (addr, info); - sprintf (dinfo, "0x%04x", insn); + if (insn == (unsigned short) -1) + { + prin (stream, ".word 0xffff; ????"); + return 2; + } if (((int) addr & 0xffff) > 0xffdf) { @@ -688,6 +872,20 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) *comm1 = 0; *comm2 = 0; + /* Check for an extension word. */ + if ((insn & 0xf800) == 0x1800) + { + extension_word = insn; + addr += 2; + insn = msp430dis_opcode (addr, info); + if (insn == (unsigned short) -1) + { + prin (stream, ".word 0x%04x, 0xffff; ????", + extension_word); + return 4; + } + } + for (opcode = msp430_opcodes; opcode->name; opcode++) { if ((insn & opcode->bin_mask) == opcode->bin_opcode @@ -699,10 +897,11 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) *comm2 = 0; /* r0 as destination. Ad should be zero. */ - if (opcode->insn_opnumb == 3 && (insn & 0x000f) == 0 - && (0x0080 & insn) == 0) + if (opcode->insn_opnumb == 3 + && (insn & 0x000f) == 0 + && (insn & 0x0080) == 0) { - cmd_len = + cmd_len += msp430_branchinstr (info, opcode, addr, insn, op1, comm1, &cycles); if (cmd_len) @@ -711,22 +910,244 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) switch (opcode->insn_opnumb) { + int n; + int reg; + + case 4: + cmd_len += msp430x_calla_instr (info, addr, insn, + op1, comm1, & cycles); + break; + + case 5: /* PUSHM/POPM */ + n = (insn & 0xf0) >> 4; + reg = (insn & 0xf); + + sprintf (op1, "#%d", n + 1); + if (opcode->bin_opcode == 0x1400) + /* PUSHM */ + sprintf (op2, "r%d", reg); + else + /* POPM */ + sprintf (op2, "r%d", reg + n); + if (insn & 0x100) + sprintf (comm1, "16-bit words"); + else + { + sprintf (comm1, "20-bit words"); + bc =".a"; + } + + cycles = 2; /*FIXME*/ + cmd_len = 2; + break; + + case 6: /* RRAM, RRCM, RRUM, RLAM. */ + n = ((insn >> 10) & 0x3) + 1; + reg = (insn & 0xf); + if ((insn & 0x10) == 0) + bc =".a"; + sprintf (op1, "#%d", n); + sprintf (op2, "r%d", reg); + cycles = 2; /*FIXME*/ + cmd_len = 2; + break; + + case 8: /* ADDA, CMPA, SUBA. */ + reg = (insn & 0xf); + n = (insn >> 8) & 0xf; + if (insn & 0x40) + { + sprintf (op1, "r%d", n); + cmd_len = 2; + } + else + { + n <<= 16; + n |= msp430dis_opcode (addr + 2, info); + sprintf (op1, "#%d", n); + if (n > 9 || n < 0) + sprintf (comm1, "0x%05x", n); + cmd_len = 4; + } + sprintf (op2, "r%d", reg); + cycles = 2; /*FIXME*/ + break; + + case 9: /* MOVA */ + reg = (insn & 0xf); + n = (insn >> 8) & 0xf; + switch ((insn >> 4) & 0xf) + { + case 0: /* MOVA @Rsrc, Rdst */ + cmd_len = 2; + sprintf (op1, "@r%d", n); + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + break; + + case 1: /* MOVA @Rsrc+, Rdst */ + cmd_len = 2; + if (strcmp (opcode->name, "reta") != 0) + { + sprintf (op1, "@r%d+", n); + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + } + break; + + case 2: /* MOVA &abs20, Rdst */ + cmd_len = 4; + n <<= 16; + n |= msp430dis_opcode (addr + 2, info); + sprintf (op1, "&%d", n); + if (n > 9 || n < 0) + sprintf (comm1, "0x%05x", n); + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + break; + + case 3: /* MOVA x(Rsrc), Rdst */ + cmd_len = 4; + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + reg = n; + n = msp430dis_opcode (addr + 2, info); + if (n & 0x8000) + n |= -1 << 16; + sprintf (op1, "%d(r%d)", n, reg); + if (n > 9 || n < 0) + { + if (reg == 0) + sprintf (comm1, "PC rel. 0x%05lx", + (long) (addr + 2 + n)); + else + sprintf (comm1, "0x%05x", n); + } + break; + + case 6: /* MOVA Rsrc, &abs20 */ + cmd_len = 4; + reg <<= 16; + reg |= msp430dis_opcode (addr + 2, info); + sprintf (op1, "r%d", n); + sprintf (op2, "&%d", reg); + if (reg > 9 || reg < 0) + sprintf (comm2, "0x%05x", reg); + break; + + case 7: /* MOVA Rsrc, x(Rdst) */ + cmd_len = 4; + sprintf (op1, "r%d", n); + n = msp430dis_opcode (addr + 2, info); + if (n & 0x8000) + n |= -1 << 16; + sprintf (op2, "%d(r%d)", n, reg); + if (n > 9 || n < 0) + { + if (reg == 0) + sprintf (comm2, "PC rel. 0x%05lx", + (long) (addr + 2 + n)); + else + sprintf (comm2, "0x%05x", n); + } + break; + + case 8: /* MOVA #imm20, Rdst */ + cmd_len = 4; + n <<= 16; + n |= msp430dis_opcode (addr + 2, info); + if (n & 0x80000) + n |= -1 << 20; + sprintf (op1, "#%d", n); + if (n > 9 || n < 0) + sprintf (comm1, "0x%05x", n); + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + break; + + case 12: /* MOVA Rsrc, Rdst */ + cmd_len = 2; + sprintf (op1, "r%d", n); + if (strcmp (opcode->name, "bra") != 0) + sprintf (op2, "r%d", reg); + break; + + default: + break; + } + cycles = 2; /* FIXME */ + break; + } + + if (cmd_len) + break; + + switch (opcode->insn_opnumb) + { case 0: - cmd_len = msp430_nooperands (opcode, addr, insn, comm1, &cycles); + cmd_len += msp430_nooperands (opcode, addr, insn, comm1, &cycles); break; case 2: - cmd_len = + cmd_len += msp430_doubleoperand (info, opcode, addr, insn, op1, op2, - comm1, comm2, &cycles); + comm1, comm2, + extension_word, + &cycles); if (insn & BYTE_OPERATION) - bc = ".b"; + { + if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0)) + bc = ".a"; + else + bc = ".b"; + } + else if (extension_word) + { + if (extension_word & (1 << 6)) + bc = ".w"; + else + { + bc = ".?"; + sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); + } + } + break; case 1: - cmd_len = + cmd_len += msp430_singleoperand (info, opcode, addr, insn, op1, comm1, + extension_word, &cycles); - if (insn & BYTE_OPERATION && opcode->fmt != 3) - bc = ".b"; + if (extension_word + && (strcmp (opcode->name, "swpb") == 0 + || strcmp (opcode->name, "sxt") == 0)) + { + if (insn & BYTE_OPERATION) + { + bc = ".?"; + sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); + } + else if (extension_word & BYTE_OPERATION) + bc = ".w"; + else + bc = ".a"; + } + else if (insn & BYTE_OPERATION && opcode->fmt != 3) + { + if (extension_word != 0 && ((extension_word & BYTE_OPERATION) == 0)) + bc = ".a"; + else + bc = ".b"; + } + else if (extension_word) + { + if (extension_word & (1 << 6)) + bc = ".w"; + else + { + bc = ".?"; + sprintf (comm2, _("Reserved use of A/L and B/W bits detected")); + } + } break; default: break; @@ -737,16 +1158,33 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) break; } - dinfo[5] = 0; - if (cmd_len < 1) { /* Unknown opcode, or invalid combination of operands. */ + if (extension_word) + { + prin (stream, ".word 0x%04x, 0x%04x; ????", extension_word, PS (insn)); + if (*comm1) + prin (stream, "\t %s", comm1); + return 4; + } (*prin) (stream, ".word 0x%04x; ????", PS (insn)); return 2; } - (*prin) (stream, "%s%s", opcode->name, bc); + /* Display the repeat count (if set) for extended register mode. */ + if (cmd_len == 2 && ((extension_word & 0xf) != 0)) + { + if (extension_word & (1 << 7)) + prin (stream, "rpt r%d { ", extension_word & 0xf); + else + prin (stream, "rpt #%d { ", (extension_word & 0xf) + 1); + } + + if (extension_word && opcode->name[strlen (opcode->name) - 1] != 'x') + (*prin) (stream, "%sx%s", opcode->name, bc); + else + (*prin) (stream, "%s%s", opcode->name, bc); if (*op1) (*prin) (stream, "\t%s", op1); @@ -783,5 +1221,9 @@ print_insn_msp430 (bfd_vma addr, disassemble_info *info) (*prin) (stream, ","); if (*comm2) (*prin) (stream, " %s", comm2); + + if (extension_word) + cmd_len += 2; + return cmd_len; } -- 2.7.4