Support for ARM "group relocations" numbers 4, and 57 through 83.
authorMark Shinwell <shinwell@codesourcery.com>
Thu, 15 Jun 2006 15:53:11 +0000 (15:53 +0000)
committerMark Shinwell <shinwell@codesourcery.com>
Thu, 15 Jun 2006 15:53:11 +0000 (15:53 +0000)
* include/elf/arm.h: Correct names of R_ARM_LDC_G{0,1,2}
to R_ARM_LDC_SB_G{0,1,2} respectively.

bfd/
* bfd-in2.h: Regenerate.
* elf32-arm.c (R_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G0,
R_ARM_ALU_PC_G1_NC, R_ARM_ALU_PC_G1, R_ARM_ALU_PC_G2,
R_ARM_LDR_PC_G1, R_ARM_LDR_PC_G2, R_ARM_LDRS_PC_G0,
R_ARM_LDRS_PC_G1, R_ARM_LDRS_PC_G2, R_ARM_LDC_PC_G0,
R_ARM_LDC_PC_G1, R_ARM_LDC_PC_G2, R_ARM_ALU_SB_G0_NC,
R_ARM_ALU_SB_G0, R_ARM_ALU_SB_G1_NC, R_ARM_ALU_SB_G1,
R_ARM_ALU_SB_G2, R_ARM_LDR_SB_G0, R_ARM_LDR_SB_G1,
R_ARM_LDR_SB_G2, R_ARM_LDRS_SB_G0, R_ARM_LDRS_SB_G1,
R_ARM_LDRS_SB_G2, R_ARM_LDC_SB_G0, R_ARM_LDC_SB_G1,
R_ARM_LDC_SB_G2): New relocation types.
(R_ARM_PC13): Rename to AAELF name R_ARM_LDR_PC_G0 and
adjust HOWTO entry to be consistent with R_ARM_LDR_PC_G1
and friends.
(elf32_arm_howto_table_3): Delete; contents merged into
elf32_arm_howto_table_2.
(elf32_arm_howto_from_type): Adjust correspondingly.
(elf32_arm_reloc_map): Extend with the above relocations.
(calculate_group_reloc_mask): New function.
(identify_add_or_sub): New function.
(elf32_arm_final_link_relocate): Support for the above
relocations.
* reloc.c: Add enumeration entries for BFD_RELOC_ARM_...
codes to correspond to the above relocations.

gas/
* config/tc-arm.c (enum parse_operand_result): New.
(struct group_reloc_table_entry): New.
(enum group_reloc_type): New.
(group_reloc_table): New array.
(find_group_reloc_table_entry): New function.
(parse_shifter_operand_group_reloc): New function.
(parse_address_main): New function, incorporating code
from the old parse_address function.  To be used via...
(parse_address): wrapper for parse_address_main; and
(parse_address_group_reloc): new function, likewise.
(enum operand_parse_code): New codes OP_SHG, OP_ADDRGLDR,
OP_ADDRGLDRS, OP_ADDRGLDC.
(parse_operands): Support for these new operand codes.
New macro po_misc_or_fail_no_backtrack.
(encode_arm_cp_address): Preserve group relocations.
(insns): Modify to use the above operand codes where group
relocations are permitted.
(md_apply_fix): Handle the group relocations
ALU_PC_G0_NC through LDC_SB_G2.
(tc_gen_reloc): Likewise.
(arm_force_relocation): Leave group relocations for the linker.
(arm_fix_adjustable): Likewise.

gas/testsuite/
* gas/arm/group-reloc-alu.d: New test.
* gas/arm/group-reloc-alu-encoding-bad.d: New test.
* gas/arm/group-reloc-alu-encoding-bad.l: New test.
* gas/arm/group-reloc-alu-encoding-bad.s: New test.
* gas/arm/group-reloc-alu-parsing-bad.d: New test.
* gas/arm/group-reloc-alu-parsing-bad.l: New test.
* gas/arm/group-reloc-alu-parsing-bad.s: New test.
* gas/arm/group-reloc-alu.s: New test.
* gas/arm/group-reloc-ldc.d: New test.
* gas/arm/group-reloc-ldc-encoding-bad.d: New test.
* gas/arm/group-reloc-ldc-encoding-bad.l: New test.
* gas/arm/group-reloc-ldc-encoding-bad.s: New test.
* gas/arm/group-reloc-ldc-parsing-bad.d: New test.
* gas/arm/group-reloc-ldc-parsing-bad.l: New test.
* gas/arm/group-reloc-ldc-parsing-bad.s: New test.
* gas/arm/group-reloc-ldc.s: New test.
* gas/arm/group-reloc-ldr.d: New test.
* gas/arm/group-reloc-ldr-encoding-bad.d: New test.
* gas/arm/group-reloc-ldr-encoding-bad.l: New test.
* gas/arm/group-reloc-ldr-encoding-bad.s: New test.
* gas/arm/group-reloc-ldr-parsing-bad.d: New test.
* gas/arm/group-reloc-ldr-parsing-bad.l: New test.
* gas/arm/group-reloc-ldr-parsing-bad.s: New test.
* gas/arm/group-reloc-ldr.s: New test.
* gas/arm/group-reloc-ldrs.d: New test.
* gas/arm/group-reloc-ldrs-encoding-bad.d: New test.
* gas/arm/group-reloc-ldrs-encoding-bad.l: New test.
* gas/arm/group-reloc-ldrs-encoding-bad.s: New test.
* gas/arm/group-reloc-ldrs-parsing-bad.d: New test.
* gas/arm/group-reloc-ldrs-parsing-bad.l: New test.
* gas/arm/group-reloc-ldrs-parsing-bad.s: New test.
* gas/arm/group-reloc-ldrs.s: New test.

ld/testsuite/
* ld-arm/group-relocs-alu-bad.d: New test.
* ld-arm/group-relocs-alu-bad.s: New test.
* ld-arm/group-relocs.d: New test.
* ld-arm/group-relocs-ldc-bad.d: New test.
* ld-arm/group-relocs-ldc-bad.s: New test.
* ld-arm/group-relocs-ldr-bad.d: New test.
* ld-arm/group-relocs-ldr-bad.s: New test.
* ld-arm/group-relocs-ldrs-bad.d: New test.
* ld-arm/group-relocs-ldrs-bad.s: New test.
* ld-arm/group-relocs.s: New test.
* ld-arm/arm-elf.exp: Wire in new tests.

ChangeLog.csl
bfd/bfd-in2.h
bfd/elf32-arm.c
bfd/libbfd.h
bfd/libcoff.h
bfd/reloc.c
gas/config/tc-arm.c
include/elf/arm.h
ld/testsuite/ld-arm/arm-elf.exp

index e6511b0..173cfc5 100644 (file)
@@ -1,3 +1,105 @@
+2006-06-15  Mark Shinwell  <shinwell@codesourcery.com>
+
+       * include/elf/arm.h: Correct names of R_ARM_LDC_G{0,1,2}
+       to R_ARM_LDC_SB_G{0,1,2} respectively.
+
+       bfd/
+       * bfd-in2.h: Regenerate.
+       * elf32-arm.c (R_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G0,
+       R_ARM_ALU_PC_G1_NC, R_ARM_ALU_PC_G1, R_ARM_ALU_PC_G2,
+       R_ARM_LDR_PC_G1, R_ARM_LDR_PC_G2, R_ARM_LDRS_PC_G0,
+       R_ARM_LDRS_PC_G1, R_ARM_LDRS_PC_G2, R_ARM_LDC_PC_G0,
+       R_ARM_LDC_PC_G1, R_ARM_LDC_PC_G2, R_ARM_ALU_SB_G0_NC,
+       R_ARM_ALU_SB_G0, R_ARM_ALU_SB_G1_NC, R_ARM_ALU_SB_G1,
+       R_ARM_ALU_SB_G2, R_ARM_LDR_SB_G0, R_ARM_LDR_SB_G1,
+       R_ARM_LDR_SB_G2, R_ARM_LDRS_SB_G0, R_ARM_LDRS_SB_G1,
+       R_ARM_LDRS_SB_G2, R_ARM_LDC_SB_G0, R_ARM_LDC_SB_G1,
+       R_ARM_LDC_SB_G2): New relocation types.
+       (R_ARM_PC13): Rename to AAELF name R_ARM_LDR_PC_G0 and
+       adjust HOWTO entry to be consistent with R_ARM_LDR_PC_G1
+       and friends.
+       (elf32_arm_howto_table_3): Delete; contents merged into
+       elf32_arm_howto_table_2.
+       (elf32_arm_howto_from_type): Adjust correspondingly.
+       (elf32_arm_reloc_map): Extend with the above relocations.
+       (calculate_group_reloc_mask): New function.
+       (identify_add_or_sub): New function.
+       (elf32_arm_final_link_relocate): Support for the above
+       relocations.
+       * reloc.c: Add enumeration entries for BFD_RELOC_ARM_...
+       codes to correspond to the above relocations.
+
+       gas/
+       * config/tc-arm.c (enum parse_operand_result): New.
+       (struct group_reloc_table_entry): New.
+       (enum group_reloc_type): New.
+       (group_reloc_table): New array.
+       (find_group_reloc_table_entry): New function.
+       (parse_shifter_operand_group_reloc): New function.
+       (parse_address_main): New function, incorporating code
+       from the old parse_address function.  To be used via...
+       (parse_address): wrapper for parse_address_main; and
+       (parse_address_group_reloc): new function, likewise.
+       (enum operand_parse_code): New codes OP_SHG, OP_ADDRGLDR,
+       OP_ADDRGLDRS, OP_ADDRGLDC.
+       (parse_operands): Support for these new operand codes.
+       New macro po_misc_or_fail_no_backtrack.
+       (encode_arm_cp_address): Preserve group relocations.
+       (insns): Modify to use the above operand codes where group
+       relocations are permitted.
+       (md_apply_fix): Handle the group relocations
+       ALU_PC_G0_NC through LDC_SB_G2.
+       (tc_gen_reloc): Likewise.
+       (arm_force_relocation): Leave group relocations for the linker.
+       (arm_fix_adjustable): Likewise.
+
+       gas/testsuite/
+       * gas/arm/group-reloc-alu.d: New test.
+       * gas/arm/group-reloc-alu-encoding-bad.d: New test.
+       * gas/arm/group-reloc-alu-encoding-bad.l: New test.
+       * gas/arm/group-reloc-alu-encoding-bad.s: New test.
+       * gas/arm/group-reloc-alu-parsing-bad.d: New test.
+       * gas/arm/group-reloc-alu-parsing-bad.l: New test.
+       * gas/arm/group-reloc-alu-parsing-bad.s: New test.
+       * gas/arm/group-reloc-alu.s: New test.
+       * gas/arm/group-reloc-ldc.d: New test.
+       * gas/arm/group-reloc-ldc-encoding-bad.d: New test.
+       * gas/arm/group-reloc-ldc-encoding-bad.l: New test.
+       * gas/arm/group-reloc-ldc-encoding-bad.s: New test.
+       * gas/arm/group-reloc-ldc-parsing-bad.d: New test.
+       * gas/arm/group-reloc-ldc-parsing-bad.l: New test.
+       * gas/arm/group-reloc-ldc-parsing-bad.s: New test.
+       * gas/arm/group-reloc-ldc.s: New test.
+       * gas/arm/group-reloc-ldr.d: New test.
+       * gas/arm/group-reloc-ldr-encoding-bad.d: New test.
+       * gas/arm/group-reloc-ldr-encoding-bad.l: New test.
+       * gas/arm/group-reloc-ldr-encoding-bad.s: New test.
+       * gas/arm/group-reloc-ldr-parsing-bad.d: New test.
+       * gas/arm/group-reloc-ldr-parsing-bad.l: New test.
+       * gas/arm/group-reloc-ldr-parsing-bad.s: New test.
+       * gas/arm/group-reloc-ldr.s: New test.
+       * gas/arm/group-reloc-ldrs.d: New test.
+       * gas/arm/group-reloc-ldrs-encoding-bad.d: New test.
+       * gas/arm/group-reloc-ldrs-encoding-bad.l: New test.
+       * gas/arm/group-reloc-ldrs-encoding-bad.s: New test.
+       * gas/arm/group-reloc-ldrs-parsing-bad.d: New test.
+       * gas/arm/group-reloc-ldrs-parsing-bad.l: New test.
+       * gas/arm/group-reloc-ldrs-parsing-bad.s: New test.
+       * gas/arm/group-reloc-ldrs.s: New test.
+
+       ld/testsuite/
+       * ld-arm/group-relocs-alu-bad.d: New test.
+       * ld-arm/group-relocs-alu-bad.s: New test.
+       * ld-arm/group-relocs.d: New test.
+       * ld-arm/group-relocs-ldc-bad.d: New test.
+       * ld-arm/group-relocs-ldc-bad.s: New test.
+       * ld-arm/group-relocs-ldr-bad.d: New test.
+       * ld-arm/group-relocs-ldr-bad.s: New test.
+       * ld-arm/group-relocs-ldrs-bad.d: New test.
+       * ld-arm/group-relocs-ldrs-bad.s: New test.
+       * ld-arm/group-relocs.s: New test.
+       * ld-arm/arm-elf.exp: Wire in new tests.
+
 2006-06-15  Julian Brown  <julian@codesourcery.com>
 
        gas/
index 0e90052..ffb6e8e 100644 (file)
@@ -2926,6 +2926,36 @@ pc-relative or some form of GOT-indirect relocation.  */
   BFD_RELOC_ARM_TLS_IE32,
   BFD_RELOC_ARM_TLS_LE32,
 
+/* ARM group relocations.  */
+  BFD_RELOC_ARM_ALU_PC_G0_NC,
+  BFD_RELOC_ARM_ALU_PC_G0,
+  BFD_RELOC_ARM_ALU_PC_G1_NC,
+  BFD_RELOC_ARM_ALU_PC_G1,
+  BFD_RELOC_ARM_ALU_PC_G2,
+  BFD_RELOC_ARM_LDR_PC_G0,
+  BFD_RELOC_ARM_LDR_PC_G1,
+  BFD_RELOC_ARM_LDR_PC_G2,
+  BFD_RELOC_ARM_LDRS_PC_G0,
+  BFD_RELOC_ARM_LDRS_PC_G1,
+  BFD_RELOC_ARM_LDRS_PC_G2,
+  BFD_RELOC_ARM_LDC_PC_G0,
+  BFD_RELOC_ARM_LDC_PC_G1,
+  BFD_RELOC_ARM_LDC_PC_G2,
+  BFD_RELOC_ARM_ALU_SB_G0_NC,
+  BFD_RELOC_ARM_ALU_SB_G0,
+  BFD_RELOC_ARM_ALU_SB_G1_NC,
+  BFD_RELOC_ARM_ALU_SB_G1,
+  BFD_RELOC_ARM_ALU_SB_G2,
+  BFD_RELOC_ARM_LDR_SB_G0,
+  BFD_RELOC_ARM_LDR_SB_G1,
+  BFD_RELOC_ARM_LDR_SB_G2,
+  BFD_RELOC_ARM_LDRS_SB_G0,
+  BFD_RELOC_ARM_LDRS_SB_G1,
+  BFD_RELOC_ARM_LDRS_SB_G2,
+  BFD_RELOC_ARM_LDC_SB_G0,
+  BFD_RELOC_ARM_LDC_SB_G1,
+  BFD_RELOC_ARM_LDC_SB_G2,
+
 /* These relocs are only used within the ARM assembler.  They are not
 (at present) written to any object files.  */
   BFD_RELOC_ARM_IMMEDIATE,
index 3a1b139..3c60384 100644 (file)
@@ -130,19 +130,19 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         TRUE),                 /* pcrel_offset */
 
   /* 8 bit absolute - R_ARM_LDR_PC_G0 in AAELF */
-  HOWTO (R_ARM_PC13,           /* type */
+  HOWTO (R_ARM_LDR_PC_G0,      /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        8,                     /* bitsize */
-        FALSE,                 /* pc_relative */
+        32,                    /* bitsize */
+        TRUE,                  /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,/* complain_on_overflow */
+        complain_overflow_dont,/* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_ARM_PC13",          /* name */
+        "R_ARM_LDR_PC_G0",     /* name */
         FALSE,                 /* partial_inplace */
-        0x000000ff,            /* src_mask */
-        0x000000ff,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
 
    /* 16 bit absolute */
   HOWTO (R_ARM_ABS16,          /* type */
@@ -884,13 +884,389 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         0xffffffff,            /* src_mask */
         0xffffffff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
-};
 
-/* Relocations 57 .. 83 are the "group relocations" which we do not
-   support.  */
+  /* Group relocations.  */
+
+  HOWTO (R_ARM_ALU_PC_G0_NC,   /* 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_ARM_ALU_PC_G0_NC",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_PC_G0,      /* 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_ARM_ALU_PC_G0",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_PC_G1_NC,   /* 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_ARM_ALU_PC_G1_NC",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_PC_G1,      /* 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_ARM_ALU_PC_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_PC_G2,      /* 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_ARM_ALU_PC_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDR_PC_G1,      /* 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_ARM_LDR_PC_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDR_PC_G2,      /* 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_ARM_LDR_PC_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_PC_G0,     /* 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_ARM_LDRS_PC_G0",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_PC_G1,     /* 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_ARM_LDRS_PC_G1",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_PC_G2,     /* 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_ARM_LDRS_PC_G2",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_PC_G0,      /* 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_ARM_LDC_PC_G0",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_PC_G1,      /* 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_ARM_LDC_PC_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_PC_G2,      /* 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_ARM_LDC_PC_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_SB_G0_NC,           /* 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_ARM_ALU_SB_G0_NC",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_SB_G0,      /* 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_ARM_ALU_SB_G0",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_SB_G1_NC,           /* 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_ARM_ALU_SB_G1_NC",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_SB_G1,      /* 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_ARM_ALU_SB_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_ALU_SB_G2,      /* 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_ARM_ALU_SB_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDR_SB_G0,      /* 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_ARM_LDR_SB_G0",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDR_SB_G1,      /* 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_ARM_LDR_SB_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDR_SB_G2,      /* 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_ARM_LDR_SB_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_SB_G0,     /* 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_ARM_LDRS_SB_G0",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_SB_G1,     /* 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_ARM_LDRS_SB_G1",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDRS_SB_G2,     /* 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_ARM_LDRS_SB_G2",    /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_SB_G0,      /* 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_ARM_LDC_SB_G0",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_SB_G1,      /* 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_ARM_LDC_SB_G1",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  HOWTO (R_ARM_LDC_SB_G2,      /* 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_ARM_LDC_SB_G2",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* End of group relocations.  */
 
-static reloc_howto_type elf32_arm_howto_table_2[] =
-{
   HOWTO (R_ARM_MOVW_BREL_NC,   /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -1230,7 +1606,7 @@ static reloc_howto_type elf32_arm_howto_table_2[] =
 
    249-255 extended, currently unused, relocations:  */
 
-static reloc_howto_type elf32_arm_howto_table_3[4] =
+static reloc_howto_type elf32_arm_howto_table_2[4] =
 {
   HOWTO (R_ARM_RREL32,         /* type */
         0,                     /* rightshift */
@@ -1295,13 +1671,9 @@ elf32_arm_howto_from_type (unsigned int r_type)
   if (r_type < NUM_ELEM (elf32_arm_howto_table_1))
     return &elf32_arm_howto_table_1[r_type];
 
-  if (r_type >= R_ARM_MOVW_BREL_NC
-      && r_type < R_ARM_MOVW_BREL_NC + NUM_ELEM (elf32_arm_howto_table_2))
-    return &elf32_arm_howto_table_2[r_type - R_ARM_MOVW_BREL_NC];
-
   if (r_type >= R_ARM_RREL32
       && r_type < R_ARM_RREL32 + NUM_ELEM (elf32_arm_howto_table_2))
-    return &elf32_arm_howto_table_3[r_type - R_ARM_RREL32];
+    return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32];
 
   return NULL;
 }
@@ -1374,6 +1746,34 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
     {BFD_RELOC_ARM_THUMB_MOVT,      R_ARM_THM_MOVT_ABS},
     {BFD_RELOC_ARM_THUMB_MOVW_PCREL, R_ARM_THM_MOVW_PREL_NC},
     {BFD_RELOC_ARM_THUMB_MOVT_PCREL, R_ARM_THM_MOVT_PREL},
+    {BFD_RELOC_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G0_NC},
+    {BFD_RELOC_ARM_ALU_PC_G0, R_ARM_ALU_PC_G0},
+    {BFD_RELOC_ARM_ALU_PC_G1_NC, R_ARM_ALU_PC_G1_NC},
+    {BFD_RELOC_ARM_ALU_PC_G1, R_ARM_ALU_PC_G1},
+    {BFD_RELOC_ARM_ALU_PC_G2, R_ARM_ALU_PC_G2},
+    {BFD_RELOC_ARM_LDR_PC_G0, R_ARM_LDR_PC_G0},
+    {BFD_RELOC_ARM_LDR_PC_G1, R_ARM_LDR_PC_G1},
+    {BFD_RELOC_ARM_LDR_PC_G2, R_ARM_LDR_PC_G2},
+    {BFD_RELOC_ARM_LDRS_PC_G0, R_ARM_LDRS_PC_G0},
+    {BFD_RELOC_ARM_LDRS_PC_G1, R_ARM_LDRS_PC_G1},
+    {BFD_RELOC_ARM_LDRS_PC_G2, R_ARM_LDRS_PC_G2},
+    {BFD_RELOC_ARM_LDC_PC_G0, R_ARM_LDC_PC_G0},
+    {BFD_RELOC_ARM_LDC_PC_G1, R_ARM_LDC_PC_G1},
+    {BFD_RELOC_ARM_LDC_PC_G2, R_ARM_LDC_PC_G2},
+    {BFD_RELOC_ARM_ALU_SB_G0_NC, R_ARM_ALU_SB_G0_NC},
+    {BFD_RELOC_ARM_ALU_SB_G0, R_ARM_ALU_SB_G0},
+    {BFD_RELOC_ARM_ALU_SB_G1_NC, R_ARM_ALU_SB_G1_NC},
+    {BFD_RELOC_ARM_ALU_SB_G1, R_ARM_ALU_SB_G1},
+    {BFD_RELOC_ARM_ALU_SB_G2, R_ARM_ALU_SB_G2},
+    {BFD_RELOC_ARM_LDR_SB_G0, R_ARM_LDR_SB_G0},
+    {BFD_RELOC_ARM_LDR_SB_G1, R_ARM_LDR_SB_G1},
+    {BFD_RELOC_ARM_LDR_SB_G2, R_ARM_LDR_SB_G2},
+    {BFD_RELOC_ARM_LDRS_SB_G0, R_ARM_LDRS_SB_G0},
+    {BFD_RELOC_ARM_LDRS_SB_G1, R_ARM_LDRS_SB_G1},
+    {BFD_RELOC_ARM_LDRS_SB_G2, R_ARM_LDRS_SB_G2},
+    {BFD_RELOC_ARM_LDC_SB_G0, R_ARM_LDC_SB_G0},
+    {BFD_RELOC_ARM_LDC_SB_G1, R_ARM_LDC_SB_G1},
+    {BFD_RELOC_ARM_LDC_SB_G2, R_ARM_LDC_SB_G2}
   };
 
 static reloc_howto_type *
@@ -2964,6 +3364,74 @@ elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value)
   return bfd_reloc_ok;
 }
 
+/* For a given value of n, calculate the value of G_n as required to
+   deal with group relocations.  We return it in the form of an
+   encoded constant-and-rotation, together with the final residual.  If n is
+   specified as less than zero, then final_residual is filled with the
+   input value and no further action is performed.  */
+
+static bfd_vma
+calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual)
+{
+  int current_n;
+  bfd_vma g_n;
+  bfd_vma encoded_g_n = 0;
+  bfd_vma residual = value; /* Also known as Y_n.  */
+
+  for (current_n = 0; current_n <= n; current_n++)
+    {
+      int shift;
+
+      /* Calculate which part of the value to mask.  */
+      if (residual == 0)
+        shift = 0;
+      else
+        {
+          int msb;
+
+          /* Determine the most significant bit in the residual and
+             align the resulting value to a 2-bit boundary.  */
+          for (msb = 30; msb >= 0; msb -= 2)
+            if (residual & (3 << msb))
+              break;
+
+          /* The desired shift is now (msb - 6), or zero, whichever
+             is the greater.  */
+          shift = msb - 6;
+          if (shift < 0)
+            shift = 0;
+        }
+
+      /* Calculate g_n in 32-bit as well as encoded constant+rotation form.  */
+      g_n = residual & (0xff << shift);
+      encoded_g_n = (g_n >> shift)
+                    | ((g_n <= 0xff ? 0 : (32 - shift) / 2) << 8);
+
+      /* Calculate the residual for the next time around.  */
+      residual &= ~g_n;
+    }
+
+  *final_residual = residual;
+
+  return encoded_g_n;
+}
+
+/* Given an ARM instruction, determine whether it is an ADD or a SUB.
+   Returns 1 if it is an ADD, -1 if it is a SUB, and 0 otherwise.  */
+static int
+identify_add_or_sub(bfd_vma insn)
+{
+  int opcode = insn & 0x1e00000;
+
+  if (opcode == 1 << 23) /* ADD */
+    return 1;
+
+  if (opcode == 1 << 22) /* SUB */
+    return -1;
+
+  return 0;
+}
+
 /* Perform a relocation as part of a final link.  */
 
 static bfd_reloc_status_type
@@ -4187,6 +4655,397 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
       }
       return bfd_reloc_ok;
 
+    case R_ARM_ALU_PC_G0_NC:
+    case R_ARM_ALU_PC_G1_NC:
+    case R_ARM_ALU_PC_G0:
+    case R_ARM_ALU_PC_G1:
+    case R_ARM_ALU_PC_G2:
+    case R_ARM_ALU_SB_G0_NC:
+    case R_ARM_ALU_SB_G1_NC:
+    case R_ARM_ALU_SB_G0:
+    case R_ARM_ALU_SB_G1:
+    case R_ARM_ALU_SB_G2:
+      {
+       bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
+        bfd_vma pc = input_section->output_section->vma
+                    + input_section->output_offset + rel->r_offset;
+        /* sb should be the origin of the *segment* containing the symbol.
+           It is not clear how to obtain this OS-dependent value, so we
+           make an arbitrary choice of zero.  */
+        bfd_vma sb = 0;
+        bfd_vma residual;
+        bfd_vma g_n;
+       bfd_signed_vma signed_value;
+        int group = 0;
+
+        /* Determine which group of bits to select.  */
+        switch (r_type)
+          {
+          case R_ARM_ALU_PC_G0_NC:
+          case R_ARM_ALU_PC_G0:
+          case R_ARM_ALU_SB_G0_NC:
+          case R_ARM_ALU_SB_G0:
+            group = 0;
+            break;
+
+          case R_ARM_ALU_PC_G1_NC:
+          case R_ARM_ALU_PC_G1:
+          case R_ARM_ALU_SB_G1_NC:
+          case R_ARM_ALU_SB_G1:
+            group = 1;
+            break;
+
+          case R_ARM_ALU_PC_G2:
+          case R_ARM_ALU_SB_G2:
+            group = 2;
+            break;
+
+          default:
+            abort();
+          }
+
+        /* If REL, extract the addend from the insn.  If RELA, it will
+           have already been fetched for us.  */
+       if (globals->use_rel)
+          {
+            int negative;
+            bfd_vma constant = insn & 0xff;
+            bfd_vma rotation = (insn & 0xf00) >> 8;
+
+            if (rotation == 0)
+              signed_addend = constant;
+            else
+              {
+                /* Compensate for the fact that in the instruction, the
+                   rotation is stored in multiples of 2 bits.  */
+                rotation *= 2;
+
+                /* Rotate "constant" right by "rotation" bits.  */
+                signed_addend = (constant >> rotation) |
+                                (constant << (8 * sizeof (bfd_vma) - rotation));
+              }
+
+            /* Determine if the instruction is an ADD or a SUB.
+               (For REL, this determines the sign of the addend.)  */
+            negative = identify_add_or_sub (insn);
+            if (negative == 0)
+              {
+                (*_bfd_error_handler)
+                  (_("%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group relocations"),
+                  input_bfd, input_section,
+                  (long) rel->r_offset, howto->name);
+                return bfd_reloc_overflow;       
+             }
+
+            signed_addend *= negative;
+          }
+
+       /* Compute the value (X) to go in the place.  */
+        if (r_type == R_ARM_ALU_PC_G0_NC
+            || r_type == R_ARM_ALU_PC_G1_NC
+            || r_type == R_ARM_ALU_PC_G0
+            || r_type == R_ARM_ALU_PC_G1
+            || r_type == R_ARM_ALU_PC_G2)
+          /* PC relative.  */
+          signed_value = value - pc + signed_addend;
+        else
+          /* Section base relative.  */
+          signed_value = value - sb + signed_addend;
+
+        /* If the target symbol is a Thumb function, then set the
+           Thumb bit in the address.  */
+       if (sym_flags == STT_ARM_TFUNC)
+         signed_value |= 1;
+
+        /* Calculate the value of the relevant G_n, in encoded
+           constant-with-rotation format.  */
+        g_n = calculate_group_reloc_mask (abs (signed_value), group,
+                                          &residual);
+
+        /* Check for overflow if required.  */
+        if ((r_type == R_ARM_ALU_PC_G0
+             || r_type == R_ARM_ALU_PC_G1
+             || r_type == R_ARM_ALU_PC_G2
+             || r_type == R_ARM_ALU_SB_G0
+             || r_type == R_ARM_ALU_SB_G1
+             || r_type == R_ARM_ALU_SB_G2) && residual != 0)
+          {
+            (*_bfd_error_handler)
+              (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"),
+              input_bfd, input_section,
+              (long) rel->r_offset, abs (signed_value), howto->name);
+            return bfd_reloc_overflow;
+          }
+
+        /* Mask out the value and the ADD/SUB part of the opcode; take care
+           not to destroy the S bit.  */
+        insn &= 0xff1ff000;
+
+        /* Set the opcode according to whether the value to go in the
+           place is negative.  */
+        if (signed_value < 0)
+          insn |= 1 << 22;
+        else
+          insn |= 1 << 23;
+
+        /* Encode the offset.  */
+        insn |= g_n;
+
+       bfd_put_32 (input_bfd, insn, hit_data);
+      }
+      return bfd_reloc_ok;
+
+    case R_ARM_LDR_PC_G0:
+    case R_ARM_LDR_PC_G1:
+    case R_ARM_LDR_PC_G2:
+    case R_ARM_LDR_SB_G0:
+    case R_ARM_LDR_SB_G1:
+    case R_ARM_LDR_SB_G2:
+      {
+       bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
+        bfd_vma pc = input_section->output_section->vma
+                    + input_section->output_offset + rel->r_offset;
+        bfd_vma sb = 0; /* See note above.  */
+        bfd_vma residual;
+       bfd_signed_vma signed_value;
+        int group = 0;
+
+        /* Determine which groups of bits to calculate.  */
+        switch (r_type)
+          {
+          case R_ARM_LDR_PC_G0:
+          case R_ARM_LDR_SB_G0:
+            group = 0;
+            break;
+
+          case R_ARM_LDR_PC_G1:
+          case R_ARM_LDR_SB_G1:
+            group = 1;
+            break;
+
+          case R_ARM_LDR_PC_G2:
+          case R_ARM_LDR_SB_G2:
+            group = 2;
+            break;
+
+          default:
+            abort();
+          }
+
+        /* If REL, extract the addend from the insn.  If RELA, it will
+           have already been fetched for us.  */
+       if (globals->use_rel)
+          {
+            int negative = (insn & (1 << 23)) ? 1 : -1;
+            signed_addend = negative * (insn & 0xfff);
+          }
+
+       /* Compute the value (X) to go in the place.  */
+        if (r_type == R_ARM_LDR_PC_G0
+            || r_type == R_ARM_LDR_PC_G1
+            || r_type == R_ARM_LDR_PC_G2)
+          /* PC relative.  */
+          signed_value = value - pc + signed_addend;
+        else
+          /* Section base relative.  */
+          signed_value = value - sb + signed_addend;
+
+        /* Calculate the value of the relevant G_{n-1} to obtain
+           the residual at that stage.  */
+        calculate_group_reloc_mask (abs (signed_value), group - 1, &residual);
+
+        /* Check for overflow.  */
+        if (residual >= 0x1000)
+          {
+            (*_bfd_error_handler)
+              (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"),
+              input_bfd, input_section,
+              (long) rel->r_offset, abs (signed_value), howto->name);
+            return bfd_reloc_overflow;
+          }
+
+        /* Mask out the value and U bit.  */
+        insn &= 0xff7ff000;
+
+        /* Set the U bit if the value to go in the place is non-negative.  */
+        if (signed_value >= 0)
+          insn |= 1 << 23;
+
+        /* Encode the offset.  */
+        insn |= residual;
+
+       bfd_put_32 (input_bfd, insn, hit_data);
+      }
+      return bfd_reloc_ok;
+
+    case R_ARM_LDRS_PC_G0:
+    case R_ARM_LDRS_PC_G1:
+    case R_ARM_LDRS_PC_G2:
+    case R_ARM_LDRS_SB_G0:
+    case R_ARM_LDRS_SB_G1:
+    case R_ARM_LDRS_SB_G2:
+      {
+       bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
+        bfd_vma pc = input_section->output_section->vma
+                    + input_section->output_offset + rel->r_offset;
+        bfd_vma sb = 0; /* See note above.  */
+        bfd_vma residual;
+       bfd_signed_vma signed_value;
+        int group = 0;
+
+        /* Determine which groups of bits to calculate.  */
+        switch (r_type)
+          {
+          case R_ARM_LDRS_PC_G0:
+          case R_ARM_LDRS_SB_G0:
+            group = 0;
+            break;
+
+          case R_ARM_LDRS_PC_G1:
+          case R_ARM_LDRS_SB_G1:
+            group = 1;
+            break;
+
+          case R_ARM_LDRS_PC_G2:
+          case R_ARM_LDRS_SB_G2:
+            group = 2;
+            break;
+
+          default:
+            abort();
+          }
+
+        /* If REL, extract the addend from the insn.  If RELA, it will
+           have already been fetched for us.  */
+       if (globals->use_rel)
+          {
+            int negative = (insn & (1 << 23)) ? 1 : -1;
+            signed_addend = negative * (((insn & 0xf00) >> 4) + (insn & 0xf));
+          }
+
+       /* Compute the value (X) to go in the place.  */
+        if (r_type == R_ARM_LDRS_PC_G0
+            || r_type == R_ARM_LDRS_PC_G1
+            || r_type == R_ARM_LDRS_PC_G2)
+          /* PC relative.  */
+          signed_value = value - pc + signed_addend;
+        else
+          /* Section base relative.  */
+          signed_value = value - sb + signed_addend;
+
+        /* Calculate the value of the relevant G_{n-1} to obtain
+           the residual at that stage.  */
+        calculate_group_reloc_mask (abs (signed_value), group - 1, &residual);
+
+        /* Check for overflow.  */
+        if (residual >= 0x100)
+          {
+            (*_bfd_error_handler)
+              (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"),
+              input_bfd, input_section,
+              (long) rel->r_offset, abs (signed_value), howto->name);
+            return bfd_reloc_overflow;
+          }
+
+        /* Mask out the value and U bit.  */
+        insn &= 0xff7ff0f0;
+
+        /* Set the U bit if the value to go in the place is non-negative.  */
+        if (signed_value >= 0)
+          insn |= 1 << 23;
+
+        /* Encode the offset.  */
+        insn |= ((residual & 0xf0) << 4) | (residual & 0xf);
+
+       bfd_put_32 (input_bfd, insn, hit_data);
+      }
+      return bfd_reloc_ok;
+
+    case R_ARM_LDC_PC_G0:
+    case R_ARM_LDC_PC_G1:
+    case R_ARM_LDC_PC_G2:
+    case R_ARM_LDC_SB_G0:
+    case R_ARM_LDC_SB_G1:
+    case R_ARM_LDC_SB_G2:
+      {
+       bfd_vma insn = bfd_get_32 (input_bfd, hit_data);
+        bfd_vma pc = input_section->output_section->vma
+                    + input_section->output_offset + rel->r_offset;
+        bfd_vma sb = 0; /* See note above.  */
+        bfd_vma residual;
+       bfd_signed_vma signed_value;
+        int group = 0;
+
+        /* Determine which groups of bits to calculate.  */
+        switch (r_type)
+          {
+          case R_ARM_LDC_PC_G0:
+          case R_ARM_LDC_SB_G0:
+            group = 0;
+            break;
+
+          case R_ARM_LDC_PC_G1:
+          case R_ARM_LDC_SB_G1:
+            group = 1;
+            break;
+
+          case R_ARM_LDC_PC_G2:
+          case R_ARM_LDC_SB_G2:
+            group = 2;
+            break;
+
+          default:
+            abort();
+          }
+
+        /* If REL, extract the addend from the insn.  If RELA, it will
+           have already been fetched for us.  */
+       if (globals->use_rel)
+          {
+            int negative = (insn & (1 << 23)) ? 1 : -1;
+            signed_addend = negative * ((insn & 0xff) << 2);
+          }
+
+       /* Compute the value (X) to go in the place.  */
+        if (r_type == R_ARM_LDC_PC_G0
+            || r_type == R_ARM_LDC_PC_G1
+            || r_type == R_ARM_LDC_PC_G2)
+          /* PC relative.  */
+          signed_value = value - pc + signed_addend;
+        else
+          /* Section base relative.  */
+          signed_value = value - sb + signed_addend;
+
+        /* Calculate the value of the relevant G_{n-1} to obtain
+           the residual at that stage.  */
+        calculate_group_reloc_mask (abs (signed_value), group - 1, &residual);
+
+        /* Check for overflow.  (The absolute value to go in the place must be
+           divisible by four and, after having been divided by four, must
+           fit in eight bits.)  */
+        if ((residual & 0x3) != 0 || residual >= 0x400)
+          {
+            (*_bfd_error_handler)
+              (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"),
+              input_bfd, input_section,
+              (long) rel->r_offset, abs (signed_value), howto->name);
+            return bfd_reloc_overflow;
+          }
+
+        /* Mask out the value and U bit.  */
+        insn &= 0xff7fff00;
+
+        /* Set the U bit if the value to go in the place is non-negative.  */
+        if (signed_value >= 0)
+          insn |= 1 << 23;
+
+        /* Encode the offset.  */
+        insn |= residual >> 2;
+
+       bfd_put_32 (input_bfd, insn, hit_data);
+      }
+      return bfd_reloc_ok;
+
     default:
       return bfd_reloc_notsupported;
     }
index 7e61f36..a587d80 100644 (file)
@@ -7,7 +7,7 @@
    (This include file is not for users of the library.)
 
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003, 2004, 2005
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    Written by Cygnus Support.
@@ -1221,6 +1221,34 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_ARM_TLS_TPOFF32",
   "BFD_RELOC_ARM_TLS_IE32",
   "BFD_RELOC_ARM_TLS_LE32",
+  "BFD_RELOC_ARM_ALU_PC_G0_NC",
+  "BFD_RELOC_ARM_ALU_PC_G0",
+  "BFD_RELOC_ARM_ALU_PC_G1_NC",
+  "BFD_RELOC_ARM_ALU_PC_G1",
+  "BFD_RELOC_ARM_ALU_PC_G2",
+  "BFD_RELOC_ARM_LDR_PC_G0",
+  "BFD_RELOC_ARM_LDR_PC_G1",
+  "BFD_RELOC_ARM_LDR_PC_G2",
+  "BFD_RELOC_ARM_LDRS_PC_G0",
+  "BFD_RELOC_ARM_LDRS_PC_G1",
+  "BFD_RELOC_ARM_LDRS_PC_G2",
+  "BFD_RELOC_ARM_LDC_PC_G0",
+  "BFD_RELOC_ARM_LDC_PC_G1",
+  "BFD_RELOC_ARM_LDC_PC_G2",
+  "BFD_RELOC_ARM_ALU_SB_G0_NC",
+  "BFD_RELOC_ARM_ALU_SB_G0",
+  "BFD_RELOC_ARM_ALU_SB_G1_NC",
+  "BFD_RELOC_ARM_ALU_SB_G1",
+  "BFD_RELOC_ARM_ALU_SB_G2",
+  "BFD_RELOC_ARM_LDR_SB_G0",
+  "BFD_RELOC_ARM_LDR_SB_G1",
+  "BFD_RELOC_ARM_LDR_SB_G2",
+  "BFD_RELOC_ARM_LDRS_SB_G0",
+  "BFD_RELOC_ARM_LDRS_SB_G1",
+  "BFD_RELOC_ARM_LDRS_SB_G2",
+  "BFD_RELOC_ARM_LDC_SB_G0",
+  "BFD_RELOC_ARM_LDC_SB_G1",
+  "BFD_RELOC_ARM_LDC_SB_G2",
   "BFD_RELOC_ARM_IMMEDIATE",
   "BFD_RELOC_ARM_ADRL_IMMEDIATE",
   "BFD_RELOC_ARM_T32_IMMEDIATE",
index b0b271c..f756f30 100644 (file)
@@ -4,7 +4,7 @@
 
 /* BFD COFF object file private structure.
    Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005
+   2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
    Written by Cygnus Support.
 
index e7f9aed..6925bf6 100644 (file)
@@ -2739,6 +2739,65 @@ ENUMDOC
   ARM thread-local storage relocations.
 
 ENUM
+  BFD_RELOC_ARM_ALU_PC_G0_NC
+ENUMX
+  BFD_RELOC_ARM_ALU_PC_G0
+ENUMX
+  BFD_RELOC_ARM_ALU_PC_G1_NC
+ENUMX
+  BFD_RELOC_ARM_ALU_PC_G1
+ENUMX
+  BFD_RELOC_ARM_ALU_PC_G2
+ENUMX
+  BFD_RELOC_ARM_LDR_PC_G0
+ENUMX
+  BFD_RELOC_ARM_LDR_PC_G1
+ENUMX
+  BFD_RELOC_ARM_LDR_PC_G2
+ENUMX
+  BFD_RELOC_ARM_LDRS_PC_G0
+ENUMX
+  BFD_RELOC_ARM_LDRS_PC_G1
+ENUMX
+  BFD_RELOC_ARM_LDRS_PC_G2
+ENUMX
+  BFD_RELOC_ARM_LDC_PC_G0
+ENUMX
+  BFD_RELOC_ARM_LDC_PC_G1
+ENUMX
+  BFD_RELOC_ARM_LDC_PC_G2
+ENUMX
+  BFD_RELOC_ARM_ALU_SB_G0_NC
+ENUMX
+  BFD_RELOC_ARM_ALU_SB_G0
+ENUMX
+  BFD_RELOC_ARM_ALU_SB_G1_NC
+ENUMX
+  BFD_RELOC_ARM_ALU_SB_G1
+ENUMX
+  BFD_RELOC_ARM_ALU_SB_G2
+ENUMX
+  BFD_RELOC_ARM_LDR_SB_G0
+ENUMX
+  BFD_RELOC_ARM_LDR_SB_G1
+ENUMX
+  BFD_RELOC_ARM_LDR_SB_G2
+ENUMX
+  BFD_RELOC_ARM_LDRS_SB_G0
+ENUMX
+  BFD_RELOC_ARM_LDRS_SB_G1
+ENUMX
+  BFD_RELOC_ARM_LDRS_SB_G2
+ENUMX
+  BFD_RELOC_ARM_LDC_SB_G0
+ENUMX
+  BFD_RELOC_ARM_LDC_SB_G1
+ENUMX
+  BFD_RELOC_ARM_LDC_SB_G2
+ENUMDOC
+  ARM group relocations.
+
+ENUM
   BFD_RELOC_ARM_IMMEDIATE
 ENUMX
   BFD_RELOC_ARM_ADRL_IMMEDIATE
index 2129f3c..07ea0cd 100644 (file)
@@ -85,6 +85,15 @@ static struct
   unsigned       sp_restored:1;
 } unwind;
 
+/* Results from operand parsing worker functions.  */
+
+typedef enum
+{
+  PARSE_OPERAND_SUCCESS,
+  PARSE_OPERAND_FAIL,
+  PARSE_OPERAND_FAIL_NO_BACKTRACK
+} parse_operand_result;
+
 /* Bit N indicates that an R_ARM_NONE relocation has been output for
    __aeabi_unwind_cpp_prN already if set. This enables dependencies to be
    emitted only once per section, to save unnecessary bloat.  */
@@ -4267,6 +4276,168 @@ parse_shifter_operand (char **str, int i)
   return SUCCESS;
 }
 
+/* Group relocation information.  Each entry in the table contains the
+   textual name of the relocation as may appear in assembler source
+   and must end with a colon.
+   Along with this textual name are the relocation codes to be used if
+   the corresponding instruction is an ALU instruction (ADD or SUB only),
+   an LDR, an LDRS, or an LDC.  */
+
+struct group_reloc_table_entry
+{
+  const char *name;
+  int alu_code;
+  int ldr_code;
+  int ldrs_code;
+  int ldc_code;
+};
+
+typedef enum
+{
+  /* Varieties of non-ALU group relocation.  */
+
+  GROUP_LDR,
+  GROUP_LDRS,
+  GROUP_LDC
+} group_reloc_type;
+
+static struct group_reloc_table_entry group_reloc_table[] =
+  { /* Program counter relative: */
+    { "pc_g0_nc",
+      BFD_RELOC_ARM_ALU_PC_G0_NC,      /* ALU */
+      0,                               /* LDR */
+      0,                               /* LDRS */
+      0 },                             /* LDC */
+    { "pc_g0",
+      BFD_RELOC_ARM_ALU_PC_G0,         /* ALU */
+      BFD_RELOC_ARM_LDR_PC_G0,         /* LDR */
+      BFD_RELOC_ARM_LDRS_PC_G0,                /* LDRS */
+      BFD_RELOC_ARM_LDC_PC_G0 },       /* LDC */
+    { "pc_g1_nc",
+      BFD_RELOC_ARM_ALU_PC_G1_NC,      /* ALU */
+      0,                               /* LDR */
+      0,                               /* LDRS */
+      0 },                             /* LDC */
+    { "pc_g1",
+      BFD_RELOC_ARM_ALU_PC_G1,         /* ALU */
+      BFD_RELOC_ARM_LDR_PC_G1,                 /* LDR */
+      BFD_RELOC_ARM_LDRS_PC_G1,                /* LDRS */
+      BFD_RELOC_ARM_LDC_PC_G1 },       /* LDC */
+    { "pc_g2",
+      BFD_RELOC_ARM_ALU_PC_G2,         /* ALU */
+      BFD_RELOC_ARM_LDR_PC_G2,         /* LDR */
+      BFD_RELOC_ARM_LDRS_PC_G2,                /* LDRS */
+      BFD_RELOC_ARM_LDC_PC_G2 },       /* LDC */
+    /* Section base relative */
+    { "sb_g0_nc",
+      BFD_RELOC_ARM_ALU_SB_G0_NC,      /* ALU */
+      0,                               /* LDR */
+      0,                               /* LDRS */
+      0 },                             /* LDC */
+    { "sb_g0",
+      BFD_RELOC_ARM_ALU_SB_G0,         /* ALU */
+      BFD_RELOC_ARM_LDR_SB_G0,         /* LDR */
+      BFD_RELOC_ARM_LDRS_SB_G0,                /* LDRS */
+      BFD_RELOC_ARM_LDC_SB_G0 },       /* LDC */
+    { "sb_g1_nc",
+      BFD_RELOC_ARM_ALU_SB_G1_NC,      /* ALU */
+      0,                               /* LDR */
+      0,                               /* LDRS */
+      0 },                             /* LDC */
+    { "sb_g1",
+      BFD_RELOC_ARM_ALU_SB_G1,         /* ALU */
+      BFD_RELOC_ARM_LDR_SB_G1,                 /* LDR */
+      BFD_RELOC_ARM_LDRS_SB_G1,                /* LDRS */
+      BFD_RELOC_ARM_LDC_SB_G1 },       /* LDC */
+    { "sb_g2",
+      BFD_RELOC_ARM_ALU_SB_G2,         /* ALU */
+      BFD_RELOC_ARM_LDR_SB_G2,         /* LDR */
+      BFD_RELOC_ARM_LDRS_SB_G2,                /* LDRS */
+      BFD_RELOC_ARM_LDC_SB_G2 }        };      /* LDC */
+
+/* Given the address of a pointer pointing to the textual name of a group
+   relocation as may appear in assembler source, attempt to find its details
+   in group_reloc_table.  The pointer will be updated to the character after
+   the trailing colon.  On failure, FAIL will be returned; SUCCESS
+   otherwise.  On success, *entry will be updated to point at the relevant
+   group_reloc_table entry. */
+
+static int
+find_group_reloc_table_entry (char **str, struct group_reloc_table_entry **out)
+{
+  unsigned int i;
+  for (i = 0; i < ARRAY_SIZE (group_reloc_table); i++)
+    {
+      int length = strlen (group_reloc_table[i].name);
+
+      if (strncasecmp (group_reloc_table[i].name, *str, length) == 0 &&
+          (*str)[length] == ':')
+        {
+          *out = &group_reloc_table[i];
+          *str += (length + 1);
+          return SUCCESS;
+        }
+    }
+
+  return FAIL;
+}
+
+/* Parse a <shifter_operand> for an ARM data processing instruction
+   (as for parse_shifter_operand) where group relocations are allowed:
+
+      #<immediate>
+      #<immediate>, <rotate>
+      #:<group_reloc>:<expression>
+      <Rm>
+      <Rm>, <shift>
+
+   where <group_reloc> is one of the strings defined in group_reloc_table.
+   The hashes are optional.
+
+   Everything else is as for parse_shifter_operand.  */
+
+static parse_operand_result
+parse_shifter_operand_group_reloc (char **str, int i)
+{
+  /* Determine if we have the sequence of characters #: or just :
+     coming next.  If we do, then we check for a group relocation.
+     If we don't, punt the whole lot to parse_shifter_operand.  */
+
+  if (((*str)[0] == '#' && (*str)[1] == ':')
+      || (*str)[0] == ':')
+    {
+      struct group_reloc_table_entry *entry;
+
+      if ((*str)[0] == '#')
+        (*str) += 2;
+      else
+        (*str)++;
+
+      /* Try to parse a group relocation.  Anything else is an error.  */
+      if (find_group_reloc_table_entry (str, &entry) == FAIL)
+        {
+          inst.error = _("unknown group relocation");
+          return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+        }
+
+      /* We now have the group relocation table entry corresponding to
+         the name in the assembler source.  Next, we parse the expression.  */
+      if (my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX))
+        return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+      /* Record the relocation type (always the ALU variant here).  */
+      inst.reloc.type = entry->alu_code;
+      assert (inst.reloc.type != 0);
+
+      return PARSE_OPERAND_SUCCESS;
+    }
+  else
+    return parse_shifter_operand (str, i) == SUCCESS
+           ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL;
+
+  /* Never reached.  */
+}
+
 /* Parse all forms of an ARM address expression.  Information is written
    to inst.operands[i] and/or inst.reloc.
 
@@ -4299,8 +4470,9 @@ parse_shifter_operand (char **str, int i)
   It is the caller's responsibility to check for addressing modes not
   supported by the instruction, and to set inst.reloc.type.  */
 
-static int
-parse_address (char **str, int i)
+static parse_operand_result
+parse_address_main (char **str, int i, int group_relocations,
+                    group_reloc_type group_type)
 {
   char *p = *str;
   int reg;
@@ -4318,16 +4490,16 @@ parse_address (char **str, int i)
       /* else a load-constant pseudo op, no special treatment needed here */
 
       if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
-       return FAIL;
+       return PARSE_OPERAND_FAIL;
 
       *str = p;
-      return SUCCESS;
+      return PARSE_OPERAND_SUCCESS;
     }
 
   if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
     {
       inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
-      return FAIL;
+      return PARSE_OPERAND_FAIL;
     }
   inst.operands[i].reg = reg;
   inst.operands[i].isreg = 1;
@@ -4346,7 +4518,7 @@ parse_address (char **str, int i)
 
          if (skip_past_comma (&p) == SUCCESS)
            if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
-             return FAIL;
+             return PARSE_OPERAND_FAIL;
        }
       else if (skip_past_char (&p, ':') == SUCCESS)
         {
@@ -4358,7 +4530,7 @@ parse_address (char **str, int i)
           if (exp.X_op != O_constant)
             {
               inst.error = _("alignment must be constant");
-              return FAIL;
+              return PARSE_OPERAND_FAIL;
             }
           inst.operands[i].imm = exp.X_add_number << 8;
           inst.operands[i].immisalign = 1;
@@ -4372,15 +4544,68 @@ parse_address (char **str, int i)
              inst.operands[i].negative = 0;
              p--;
            }
-         if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
-           return FAIL;
+
+         if (group_relocations &&
+              ((*p == '#' && *(p + 1) == ':') || *p == ':'))
+
+           {
+             struct group_reloc_table_entry *entry;
+
+              /* Skip over the #: or : sequence.  */
+              if (*p == '#')
+                p += 2;
+              else
+                p++;
+
+             /* Try to parse a group relocation.  Anything else is an
+                 error.  */
+             if (find_group_reloc_table_entry (&p, &entry) == FAIL)
+               {
+                 inst.error = _("unknown group relocation");
+                 return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+               }
+
+             /* We now have the group relocation table entry corresponding to
+                the name in the assembler source.  Next, we parse the
+                 expression.  */
+             if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+               return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+             /* Record the relocation type.  */
+              switch (group_type)
+                {
+                  case GROUP_LDR:
+                   inst.reloc.type = entry->ldr_code;
+                    break;
+
+                  case GROUP_LDRS:
+                   inst.reloc.type = entry->ldrs_code;
+                    break;
+
+                  case GROUP_LDC:
+                   inst.reloc.type = entry->ldc_code;
+                    break;
+
+                  default:
+                    assert (0);
+                }
+
+              if (inst.reloc.type == 0)
+               {
+                 inst.error = _("this group relocation is not allowed on this instruction");
+                 return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+               }
+            }
+          else
+           if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+             return PARSE_OPERAND_FAIL;
        }
     }
 
   if (skip_past_char (&p, ']') == FAIL)
     {
       inst.error = _("']' expected");
-      return FAIL;
+      return PARSE_OPERAND_FAIL;
     }
 
   if (skip_past_char (&p, '!') == SUCCESS)
@@ -4393,20 +4618,20 @@ parse_address (char **str, int i)
          /* [Rn], {expr} - unindexed, with option */
          if (parse_immediate (&p, &inst.operands[i].imm,
                               0, 255, TRUE) == FAIL)
-           return FAIL;
+           return PARSE_OPERAND_FAIL;
 
          if (skip_past_char (&p, '}') == FAIL)
            {
              inst.error = _("'}' expected at end of 'option' field");
-             return FAIL;
+             return PARSE_OPERAND_FAIL;
            }
          if (inst.operands[i].preind)
            {
              inst.error = _("cannot combine index with option");
-             return FAIL;
+             return PARSE_OPERAND_FAIL;
            }
          *str = p;
-         return SUCCESS;
+         return PARSE_OPERAND_SUCCESS;
        }
       else
        {
@@ -4416,7 +4641,7 @@ parse_address (char **str, int i)
          if (inst.operands[i].preind)
            {
              inst.error = _("cannot combine pre- and post-indexing");
-             return FAIL;
+             return PARSE_OPERAND_FAIL;
            }
 
          if (*p == '+') p++;
@@ -4434,7 +4659,7 @@ parse_address (char **str, int i)
 
              if (skip_past_comma (&p) == SUCCESS)
                if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
-                 return FAIL;
+                 return PARSE_OPERAND_FAIL;
            }
          else
            {
@@ -4444,7 +4669,7 @@ parse_address (char **str, int i)
                  p--;
                }
              if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
-               return FAIL;
+               return PARSE_OPERAND_FAIL;
            }
        }
     }
@@ -4458,7 +4683,20 @@ parse_address (char **str, int i)
       inst.reloc.exp.X_add_number = 0;
     }
   *str = p;
-  return SUCCESS;
+  return PARSE_OPERAND_SUCCESS;
+}
+
+static int
+parse_address (char **str, int i)
+{
+  return parse_address_main (str, i, 0, 0) == PARSE_OPERAND_SUCCESS
+         ? SUCCESS : FAIL;
+}
+
+static parse_operand_result
+parse_address_group_reloc (char **str, int i, group_reloc_type type)
+{
+  return parse_address_main (str, i, 1, type);
 }
 
 /* Parse an operand for a MOVW or MOVT instruction.  */
@@ -5057,7 +5295,11 @@ enum operand_parse_code
   OP_I31b,     /*                             0 .. 31 */
 
   OP_SH,       /* shifter operand */
+  OP_SHG,      /* shifter operand with possible group relocation */
   OP_ADDR,     /* Memory address expression (any mode) */
+  OP_ADDRGLDR, /* Mem addr expr (any mode) with possible LDR group reloc */
+  OP_ADDRGLDRS, /* Mem addr expr (any mode) with possible LDRS group reloc */
+  OP_ADDRGLDC,  /* Mem addr expr (any mode) with possible LDC group reloc */
   OP_EXP,      /* arbitrary expression */
   OP_EXPi,     /* same, with optional immediate prefix */
   OP_EXPr,     /* same, with optional relocation suffix */
@@ -5112,6 +5354,7 @@ parse_operands (char *str, const unsigned char *pattern)
   const char *backtrack_error = 0;
   int i, val, backtrack_index = 0;
   enum arm_reg_type rtype;
+  parse_operand_result result;
 
 #define po_char_or_fail(chr) do {              \
   if (skip_past_char (&str, chr) == FAIL)      \
@@ -5169,6 +5412,14 @@ parse_operands (char *str, const unsigned char *pattern)
     goto failure;                              \
 } while (0)
 
+#define po_misc_or_fail_no_backtrack(expr) do {        \
+  result = expr;                               \
+  if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK)\
+    backtrack_pos = 0;                         \
+  if (result != PARSE_OPERAND_SUCCESS)         \
+    goto failure;                              \
+} while (0)
+
   skip_whitespace (str);
 
   for (i = 0; upat[i] != OP_stop; i++)
@@ -5551,10 +5802,30 @@ parse_operands (char *str, const unsigned char *pattern)
          po_misc_or_fail (parse_address (&str, i));
          break;
 
+       case OP_ADDRGLDR:
+         po_misc_or_fail_no_backtrack (
+            parse_address_group_reloc (&str, i, GROUP_LDR));
+         break;
+
+       case OP_ADDRGLDRS:
+         po_misc_or_fail_no_backtrack (
+            parse_address_group_reloc (&str, i, GROUP_LDRS));
+         break;
+
+       case OP_ADDRGLDC:
+         po_misc_or_fail_no_backtrack (
+            parse_address_group_reloc (&str, i, GROUP_LDC));
+         break;
+
        case OP_SH:
          po_misc_or_fail (parse_shifter_operand (&str, i));
          break;
 
+       case OP_SHG:
+         po_misc_or_fail_no_backtrack (
+            parse_shifter_operand_group_reloc (&str, i));
+         break;
+
        case OP_oSHll:
          po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE));
          break;
@@ -5908,7 +6179,8 @@ encode_arm_addr_mode_3 (int i, bfd_boolean is_t)
    into a coprocessor load/store instruction.  If wb_ok is false,
    reject use of writeback; if unind_ok is false, reject use of
    unindexed addressing.  If reloc_override is not 0, use it instead
-   of BFD_ARM_CP_OFF_IMM.  */
+   of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one
+   (in which case it is preserved).  */
 
 static int
 encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
@@ -5950,10 +6222,16 @@ encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
 
   if (reloc_override)
     inst.reloc.type = reloc_override;
-  else if (thumb_mode)
-    inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
-  else
-    inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+  else if ((inst.reloc.type < BFD_RELOC_ARM_ALU_PC_G0_NC
+            || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2)
+           && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0)
+    {
+      if (thumb_mode)
+        inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
+      else
+        inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+    }
+
   return SUCCESS;
 }
 
@@ -14028,8 +14306,8 @@ static const struct asm_opcode insns[] =
  tC3(eors,     0300000, eors,     3, (RR, oRR, SH), arit, t_arit3c),
  tCE(sub,      0400000, sub,      3, (RR, oRR, SH), arit, t_add_sub),
  tC3(subs,     0500000, subs,     3, (RR, oRR, SH), arit, t_add_sub),
- tCE(add,      0800000, add,      3, (RR, oRR, SH), arit, t_add_sub),
- tC3(adds,     0900000, adds,     3, (RR, oRR, SH), arit, t_add_sub),
+ tCE(add,      0800000, add,      3, (RR, oRR, SHG), arit, t_add_sub),
+ tC3(adds,     0900000, adds,     3, (RR, oRR, SHG), arit, t_add_sub),
  tCE(adc,      0a00000, adc,      3, (RR, oRR, SH), arit, t_arit3c),
  tC3(adcs,     0b00000, adcs,     3, (RR, oRR, SH), arit, t_arit3c),
  tCE(sbc,      0c00000, sbc,      3, (RR, oRR, SH), arit, t_arit3),
@@ -14057,10 +14335,10 @@ static const struct asm_opcode insns[] =
  tCE(mvn,      1e00000, mvn,      2, (RR, SH),      mov,  t_mvn_tst),
  tC3(mvns,     1f00000, mvns,     2, (RR, SH),      mov,  t_mvn_tst),
 
- tCE(ldr,      4100000, ldr,      2, (RR, ADDR),    ldst, t_ldst),
- tC3(ldrb,     4500000, ldrb,     2, (RR, ADDR),    ldst, t_ldst),
- tCE(str,      4000000, str,      2, (RR, ADDR),    ldst, t_ldst),
- tC3(strb,     4400000, strb,     2, (RR, ADDR),    ldst, t_ldst),
+ tCE(ldr,      4100000, ldr,      2, (RR, ADDRGLDR),ldst, t_ldst),
+ tC3(ldrb,     4500000, ldrb,     2, (RR, ADDRGLDR),ldst, t_ldst),
+ tCE(str,      4000000, str,      2, (RR, ADDRGLDR),ldst, t_ldst),
+ tC3(strb,     4400000, strb,     2, (RR, ADDRGLDR),ldst, t_ldst),
 
  tCE(stm,      8800000, stmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
  tC3(stmia,    8800000, stmia,    2, (RRw, REGLST), ldmstm, t_ldmstm),
@@ -14144,10 +14422,10 @@ static const struct asm_opcode insns[] =
 
   /* Generic coprocessor instructions. */
  TCE(cdp,      e000000, ee000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp,    cdp),
- TCE(ldc,      c100000, ec100000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TC3(ldcl,     c500000, ec500000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TCE(stc,      c000000, ec000000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TC3(stcl,     c400000, ec400000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TCE(ldc,      c100000, ec100000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
+ TC3(ldcl,     c500000, ec500000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
+ TCE(stc,      c000000, ec000000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
+ TC3(stcl,     c400000, ec400000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
  TCE(mcr,      e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
  TCE(mrc,      e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
 
@@ -14176,12 +14454,12 @@ static const struct asm_opcode insns[] =
 #define ARM_VARIANT &arm_ext_v4        /* ARM Architecture 4.  */
 #undef THUMB_VARIANT
 #define THUMB_VARIANT &arm_ext_v4t
- tC3(ldrh,     01000b0, ldrh,     2, (RR, ADDR), ldstv4, t_ldst),
- tC3(strh,     00000b0, strh,     2, (RR, ADDR), ldstv4, t_ldst),
- tC3(ldrsh,    01000f0, ldrsh,    2, (RR, ADDR), ldstv4, t_ldst),
- tC3(ldrsb,    01000d0, ldrsb,    2, (RR, ADDR), ldstv4, t_ldst),
- tCM(ld,sh,    01000f0, ldrsh,    2, (RR, ADDR), ldstv4, t_ldst),
- tCM(ld,sb,    01000d0, ldrsb,    2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(ldrh,     01000b0, ldrh,     2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(strh,     00000b0, strh,     2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(ldrsh,    01000f0, ldrsh,    2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(ldrsb,    01000d0, ldrsb,    2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tCM(ld,sh,    01000f0, ldrsh,    2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tCM(ld,sb,    01000d0, ldrsb,    2, (RR, ADDRGLDRS), ldstv4, t_ldst),
 
 #undef ARM_VARIANT
 #define ARM_VARIANT &arm_ext_v4t_5
@@ -14202,10 +14480,10 @@ static const struct asm_opcode insns[] =
 #undef THUMB_VARIANT
 #define THUMB_VARIANT &arm_ext_v6t2
  TCE(clz,      16f0f10, fab0f080, 2, (RRnpc, RRnpc),                   rd_rm,  t_clz),
- TUF(ldc2,     c100000, fc100000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TUF(ldc2l,    c500000, fc500000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TUF(stc2,     c000000, fc000000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
- TUF(stc2l,    c400000, fc400000, 3, (RCP, RCN, ADDR),                 lstc,   lstc),
+ TUF(ldc2,     c100000, fc100000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
+ TUF(ldc2l,    c500000, fc500000, 3, (RCP, RCN, ADDRGLDC),                     lstc,   lstc),
+ TUF(stc2,     c000000, fc000000, 3, (RCP, RCN, ADDRGLDC),             lstc,   lstc),
+ TUF(stc2l,    c400000, fc400000, 3, (RCP, RCN, ADDRGLDC),                     lstc,   lstc),
  TUF(cdp2,     e000000, fe000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp,    cdp),
  TUF(mcr2,     e000010, fe000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
  TUF(mrc2,     e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b),   co_reg, co_reg),
@@ -14241,8 +14519,8 @@ static const struct asm_opcode insns[] =
 #undef ARM_VARIANT
 #define ARM_VARIANT &arm_ext_v5e /*  ARM Architecture 5TE.  */
  TUF(pld,      450f000, f810f000, 1, (ADDR),                pld,  t_pld),
- TC3(ldrd,     00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
- TC3(strd,     00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
+ TC3(ldrd,     00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(strd,     00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
 
  TCE(mcrr,     c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
  TCE(mrrc,     c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
@@ -14458,15 +14736,15 @@ static const struct asm_opcode insns[] =
  cCE(wfc,      e400110, 1, (RR),            rd),
  cCE(rfc,      e500110, 1, (RR),            rd),
 
- cCL(ldfs,     c100100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(ldfd,     c108100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(ldfe,     c500100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(ldfp,     c508100, 2, (RF, ADDR),      rd_cpaddr),
+ cCL(ldfs,     c100100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(ldfd,     c108100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(ldfe,     c500100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(ldfp,     c508100, 2, (RF, ADDRGLDC),  rd_cpaddr),
 
- cCL(stfs,     c000100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(stfd,     c008100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(stfe,     c400100, 2, (RF, ADDR),      rd_cpaddr),
- cCL(stfp,     c408100, 2, (RF, ADDR),      rd_cpaddr),
+ cCL(stfs,     c000100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(stfd,     c008100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(stfe,     c400100, 2, (RF, ADDRGLDC),  rd_cpaddr),
+ cCL(stfp,     c408100, 2, (RF, ADDRGLDC),  rd_cpaddr),
 
  cCL(mvfs,     e008100, 2, (RF, RF_IF),     rd_rm),
  cCL(mvfsp,    e008120, 2, (RF, RF_IF),     rd_rm),
@@ -14909,8 +15187,8 @@ static const struct asm_opcode insns[] =
  cCE(fmxr,     ee00a10, 2, (RVC, RR),        rn_rd),
 
   /* Memory operations.         */
- cCE(flds,     d100a00, 2, (RVS, ADDR),      vfp_sp_ldst),
- cCE(fsts,     d000a00, 2, (RVS, ADDR),      vfp_sp_ldst),
+ cCE(flds,     d100a00, 2, (RVS, ADDRGLDC),  vfp_sp_ldst),
+ cCE(fsts,     d000a00, 2, (RVS, ADDRGLDC),  vfp_sp_ldst),
  cCE(fldmias,  c900a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
  cCE(fldmfds,  c900a00, 2, (RRw, VRSLST),    vfp_sp_ldstmia),
  cCE(fldmdbs,  d300a00, 2, (RRw, VRSLST),    vfp_sp_ldstmdb),
@@ -14968,8 +15246,8 @@ static const struct asm_opcode insns[] =
  cCE(ftouizd,  ebc0bc0, 2, (RVS, RVD),       vfp_sp_dp_cvt),
 
   /* Memory operations.         */
- cCE(fldd,     d100b00, 2, (RVD, ADDR),      vfp_dp_ldst),
- cCE(fstd,     d000b00, 2, (RVD, ADDR),      vfp_dp_ldst),
+ cCE(fldd,     d100b00, 2, (RVD, ADDRGLDC),  vfp_dp_ldst),
+ cCE(fstd,     d000b00, 2, (RVD, ADDRGLDC),  vfp_dp_ldst),
  cCE(fldmiad,  c900b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
  cCE(fldmfdd,  c900b00, 2, (RRw, VRDLST),    vfp_dp_ldstmia),
  cCE(fldmdbd,  d300b00, 2, (RRw, VRDLST),    vfp_dp_ldstmdb),
@@ -15043,8 +15321,8 @@ static const struct asm_opcode insns[] =
  NCE(vstm,      c800b00, 2, (RRw, VRSDLST), neon_ldm_stm),
  NCE(vstmia,    c800b00, 2, (RRw, VRSDLST), neon_ldm_stm),
  NCE(vstmdb,    d000b00, 2, (RRw, VRSDLST), neon_ldm_stm),
- NCE(vldr,      d100b00, 2, (RVSD, ADDR), neon_ldr_str),
- NCE(vstr,      d000b00, 2, (RVSD, ADDR), neon_ldr_str),
+ NCE(vldr,      d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
+ NCE(vstr,      d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
 
  nCEF(vcvt,     vcvt,    3, (RNSDQ, RNSDQ, oI32b), neon_cvt),
 
@@ -15500,14 +15778,14 @@ static const struct asm_opcode insns[] =
 
 #undef ARM_VARIANT
 #define ARM_VARIANT &arm_cext_maverick /* Cirrus Maverick instructions.        */
- cCE(cfldrs,   c100400, 2, (RMF, ADDR),              rd_cpaddr),
- cCE(cfldrd,   c500400, 2, (RMD, ADDR),              rd_cpaddr),
- cCE(cfldr32,  c100500, 2, (RMFX, ADDR),             rd_cpaddr),
- cCE(cfldr64,  c500500, 2, (RMDX, ADDR),             rd_cpaddr),
- cCE(cfstrs,   c000400, 2, (RMF, ADDR),              rd_cpaddr),
- cCE(cfstrd,   c400400, 2, (RMD, ADDR),              rd_cpaddr),
- cCE(cfstr32,  c000500, 2, (RMFX, ADDR),             rd_cpaddr),
- cCE(cfstr64,  c400500, 2, (RMDX, ADDR),             rd_cpaddr),
+ cCE(cfldrs,   c100400, 2, (RMF, ADDRGLDC),          rd_cpaddr),
+ cCE(cfldrd,   c500400, 2, (RMD, ADDRGLDC),          rd_cpaddr),
+ cCE(cfldr32,  c100500, 2, (RMFX, ADDRGLDC),         rd_cpaddr),
+ cCE(cfldr64,  c500500, 2, (RMDX, ADDRGLDC),         rd_cpaddr),
+ cCE(cfstrs,   c000400, 2, (RMF, ADDRGLDC),          rd_cpaddr),
+ cCE(cfstrd,   c400400, 2, (RMD, ADDRGLDC),          rd_cpaddr),
+ cCE(cfstr32,  c000500, 2, (RMFX, ADDRGLDC),         rd_cpaddr),
+ cCE(cfstr64,  c400500, 2, (RMDX, ADDRGLDC),         rd_cpaddr),
  cCE(cfmvsr,   e000450, 2, (RMF, RR),                rn_rd),
  cCE(cfmvrs,   e100450, 2, (RR, RMF),                rd_rn),
  cCE(cfmvdlr,  e000410, 2, (RMD, RR),                rn_rd),
@@ -16889,6 +17167,7 @@ md_apply_fix (fixS *    fixP,
   assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
 
   /* Note whether this will delete the relocation.  */
+
   if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
     fixP->fx_done = 1;
 
@@ -17760,6 +18039,175 @@ md_apply_fix (fixS *  fixP,
        }
       return;
 
+   case BFD_RELOC_ARM_ALU_PC_G0_NC:
+   case BFD_RELOC_ARM_ALU_PC_G0:
+   case BFD_RELOC_ARM_ALU_PC_G1_NC:
+   case BFD_RELOC_ARM_ALU_PC_G1:
+   case BFD_RELOC_ARM_ALU_PC_G2:
+   case BFD_RELOC_ARM_ALU_SB_G0_NC:
+   case BFD_RELOC_ARM_ALU_SB_G0:
+   case BFD_RELOC_ARM_ALU_SB_G1_NC:
+   case BFD_RELOC_ARM_ALU_SB_G1:
+   case BFD_RELOC_ARM_ALU_SB_G2:
+     assert (!fixP->fx_done);
+     if (!seg->use_rela_p)
+       {
+         bfd_vma insn;
+         bfd_vma encoded_addend;
+         bfd_vma addend_abs = abs (value);
+
+         /* Check that the absolute value of the addend can be
+            expressed as an 8-bit constant plus a rotation.  */
+         encoded_addend = encode_arm_immediate (addend_abs);
+         if (encoded_addend == (unsigned int) FAIL)
+          as_bad_where (fixP->fx_file, fixP->fx_line,
+                        _("the offset 0x%08lX is not representable"),
+                         addend_abs);
+
+         /* Extract the instruction.  */
+         insn = md_chars_to_number (buf, INSN_SIZE);
+
+         /* If the addend is positive, use an ADD instruction.
+            Otherwise use a SUB.  Take care not to destroy the S bit.  */
+         insn &= 0xff1fffff;
+         if (value < 0)
+           insn |= 1 << 22;
+         else
+           insn |= 1 << 23;
+
+         /* Place the encoded addend into the first 12 bits of the
+            instruction.  */
+         insn &= 0xfffff000;
+         insn |= encoded_addend;
+   
+         /* Update the instruction.  */  
+         md_number_to_chars (buf, insn, INSN_SIZE);
+       }
+     break;
+
+    case BFD_RELOC_ARM_LDR_PC_G0:
+    case BFD_RELOC_ARM_LDR_PC_G1:
+    case BFD_RELOC_ARM_LDR_PC_G2:
+    case BFD_RELOC_ARM_LDR_SB_G0:
+    case BFD_RELOC_ARM_LDR_SB_G1:
+    case BFD_RELOC_ARM_LDR_SB_G2:
+      assert (!fixP->fx_done);
+      if (!seg->use_rela_p)
+        {
+          bfd_vma insn;
+          bfd_vma addend_abs = abs (value);
+
+          /* Check that the absolute value of the addend can be
+             encoded in 12 bits.  */
+          if (addend_abs >= 0x1000)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("bad offset 0x%08lX (only 12 bits available for the magnitude)"),
+                          addend_abs);
+
+          /* Extract the instruction.  */
+          insn = md_chars_to_number (buf, INSN_SIZE);
+
+          /* If the addend is negative, clear bit 23 of the instruction.
+             Otherwise set it.  */
+          if (value < 0)
+            insn &= ~(1 << 23);
+          else
+            insn |= 1 << 23;
+
+          /* Place the absolute value of the addend into the first 12 bits
+             of the instruction.  */
+          insn &= 0xfffff000;
+          insn |= addend_abs;
+    
+          /* Update the instruction.  */  
+          md_number_to_chars (buf, insn, INSN_SIZE);
+        }
+      break;
+
+    case BFD_RELOC_ARM_LDRS_PC_G0:
+    case BFD_RELOC_ARM_LDRS_PC_G1:
+    case BFD_RELOC_ARM_LDRS_PC_G2:
+    case BFD_RELOC_ARM_LDRS_SB_G0:
+    case BFD_RELOC_ARM_LDRS_SB_G1:
+    case BFD_RELOC_ARM_LDRS_SB_G2:
+      assert (!fixP->fx_done);
+      if (!seg->use_rela_p)
+        {
+          bfd_vma insn;
+          bfd_vma addend_abs = abs (value);
+
+          /* Check that the absolute value of the addend can be
+             encoded in 8 bits.  */
+          if (addend_abs >= 0x100)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("bad offset 0x%08lX (only 8 bits available for the magnitude)"),
+                          addend_abs);
+
+          /* Extract the instruction.  */
+          insn = md_chars_to_number (buf, INSN_SIZE);
+
+          /* If the addend is negative, clear bit 23 of the instruction.
+             Otherwise set it.  */
+          if (value < 0)
+            insn &= ~(1 << 23);
+          else
+            insn |= 1 << 23;
+
+          /* Place the first four bits of the absolute value of the addend
+             into the first 4 bits of the instruction, and the remaining
+             four into bits 8 .. 11.  */
+          insn &= 0xfffff0f0;
+          insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4);
+    
+          /* Update the instruction.  */  
+          md_number_to_chars (buf, insn, INSN_SIZE);
+        }
+      break;
+
+    case BFD_RELOC_ARM_LDC_PC_G0:
+    case BFD_RELOC_ARM_LDC_PC_G1:
+    case BFD_RELOC_ARM_LDC_PC_G2:
+    case BFD_RELOC_ARM_LDC_SB_G0:
+    case BFD_RELOC_ARM_LDC_SB_G1:
+    case BFD_RELOC_ARM_LDC_SB_G2:
+      assert (!fixP->fx_done);
+      if (!seg->use_rela_p)
+        {
+          bfd_vma insn;
+          bfd_vma addend_abs = abs (value);
+
+          /* Check that the absolute value of the addend is a multiple of
+             four and, when divided by four, fits in 8 bits.  */
+          if (addend_abs & 0x3)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("bad offset 0x%08lX (must be word-aligned)"),
+                          addend_abs);
+
+          if ((addend_abs >> 2) > 0xff)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("bad offset 0x%08lX (must be an 8-bit number of words)"),
+                          addend_abs);
+
+          /* Extract the instruction.  */
+          insn = md_chars_to_number (buf, INSN_SIZE);
+
+          /* If the addend is negative, clear bit 23 of the instruction.
+             Otherwise set it.  */
+          if (value < 0)
+            insn &= ~(1 << 23);
+          else
+            insn |= 1 << 23;
+
+          /* Place the addend (divided by four) into the first eight
+             bits of the instruction.  */
+          insn &= 0xfffffff0;
+          insn |= addend_abs >> 2;
+    
+          /* Update the instruction.  */  
+          md_number_to_chars (buf, insn, INSN_SIZE);
+        }
+      break;
+
     case BFD_RELOC_UNUSED:
     default:
       as_bad_where (fixP->fx_file, fixP->fx_line,
@@ -17879,6 +18327,34 @@ tc_gen_reloc (asection *section, fixS *fixp)
     case BFD_RELOC_ARM_TLS_LDO32:
     case BFD_RELOC_ARM_PCREL_CALL:
     case BFD_RELOC_ARM_PCREL_JUMP:
+    case BFD_RELOC_ARM_ALU_PC_G0_NC:
+    case BFD_RELOC_ARM_ALU_PC_G0:
+    case BFD_RELOC_ARM_ALU_PC_G1_NC:
+    case BFD_RELOC_ARM_ALU_PC_G1:
+    case BFD_RELOC_ARM_ALU_PC_G2:
+    case BFD_RELOC_ARM_LDR_PC_G0:
+    case BFD_RELOC_ARM_LDR_PC_G1:
+    case BFD_RELOC_ARM_LDR_PC_G2:
+    case BFD_RELOC_ARM_LDRS_PC_G0:
+    case BFD_RELOC_ARM_LDRS_PC_G1:
+    case BFD_RELOC_ARM_LDRS_PC_G2:
+    case BFD_RELOC_ARM_LDC_PC_G0:
+    case BFD_RELOC_ARM_LDC_PC_G1:
+    case BFD_RELOC_ARM_LDC_PC_G2:
+    case BFD_RELOC_ARM_ALU_SB_G0_NC:
+    case BFD_RELOC_ARM_ALU_SB_G0:
+    case BFD_RELOC_ARM_ALU_SB_G1_NC:
+    case BFD_RELOC_ARM_ALU_SB_G1:
+    case BFD_RELOC_ARM_ALU_SB_G2:
+    case BFD_RELOC_ARM_LDR_SB_G0:
+    case BFD_RELOC_ARM_LDR_SB_G1:
+    case BFD_RELOC_ARM_LDR_SB_G2:
+    case BFD_RELOC_ARM_LDRS_SB_G0:
+    case BFD_RELOC_ARM_LDRS_SB_G1:
+    case BFD_RELOC_ARM_LDRS_SB_G2:
+    case BFD_RELOC_ARM_LDC_SB_G0:
+    case BFD_RELOC_ARM_LDC_SB_G1:
+    case BFD_RELOC_ARM_LDC_SB_G2:
       code = fixp->fx_r_type;
       break;
 
@@ -18047,6 +18523,12 @@ arm_force_relocation (struct fix * fixp)
       || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
     return 0;
 
+  /* Always leave these relocations for the linker.  */
+  if ((fixp->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+       && fixp->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+      || fixp->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
+    return 1;
+
   return generic_force_reloc (fixp);
 }
 
@@ -18111,6 +18593,12 @@ arm_fix_adjustable (fixS * fixP)
       || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
     return 0;
 
+  /* Similarly for group relocations.  */
+  if ((fixP->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+       && fixP->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+      || fixP->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
+    return 0;
+
   return 1;
 }
 
index 8311c13..6b72977 100644 (file)
@@ -169,9 +169,9 @@ START_RELOC_NUMBERS (elf_arm_reloc_type)
   RELOC_NUMBER (R_ARM_LDRS_SB_G0,        78)
   RELOC_NUMBER (R_ARM_LDRS_SB_G1,        79)
   RELOC_NUMBER (R_ARM_LDRS_SB_G2,        80)
-  RELOC_NUMBER (R_ARM_LDC_G0,            81)
-  RELOC_NUMBER (R_ARM_LDC_G1,            82)
-  RELOC_NUMBER (R_ARM_LDC_G2,            83)
+  RELOC_NUMBER (R_ARM_LDC_SB_G0,         81)
+  RELOC_NUMBER (R_ARM_LDC_SB_G1,         82)
+  RELOC_NUMBER (R_ARM_LDC_SB_G2,         83)
   RELOC_NUMBER (R_ARM_MOVW_BREL_NC,             84)
   RELOC_NUMBER (R_ARM_MOVT_BREL,                85)
   RELOC_NUMBER (R_ARM_MOVW_BREL,                86)
@@ -221,7 +221,6 @@ START_RELOC_NUMBERS (elf_arm_reloc_type)
   FAKE_RELOC (R_ARM_GOT32,              R_ARM_GOT_BREL)   /* 32 bit GOT entry.  */
   FAKE_RELOC (R_ARM_ROSEGREL32,         R_ARM_SBREL31)    /* ??? */
   FAKE_RELOC (R_ARM_AMP_VCALL9,         R_ARM_BREL_ADJ)   /* Thumb-something.  Not used.  */
-  FAKE_RELOC (R_ARM_PC13,               R_ARM_LDR_PC_G0)  /* Unclear whether meaning is different.  */
 END_RELOC_NUMBERS (R_ARM_max)
 
 #ifdef BFD_ARCH_SIZE
index eedbe10..0939506 100644 (file)
@@ -59,6 +59,9 @@ if { ![is_elf_format] || ![istarget "arm*-*-*"] } {
 # readelf: Apply readelf options on result.  Compare with regex (last arg).
 
 set armelftests {
+    {"Group relocations" "-Ttext 0x8000 --section-start zero=0x0 --section-start alpha=0xeef0 --section-start beta=0xffeef0" "" {group-relocs.s}
+     {{objdump -Dr group-relocs.d}}
+     "group-relocs"}
     {"Simple non-PIC shared library" "-shared" "" {arm-lib.s}
      {{objdump -fdw arm-lib.d} {objdump -Rw arm-lib.r}}
      "arm-lib.so"}
@@ -131,3 +134,8 @@ set armelftests {
 }
 
 run_ld_link_tests $armelftests
+run_dump_test "group-relocs-alu-bad"
+run_dump_test "group-relocs-ldr-bad"
+run_dump_test "group-relocs-ldrs-bad"
+run_dump_test "group-relocs-ldc-bad"
+