public inbox for gnu-gabi@sourceware.org
 help / color / mirror / Atom feed
* [RFC] SHF_GNU_ABIND Section Flag
@ 2020-10-20 10:05 Jozef Lawrynowicz
  2020-10-23 10:20 ` Jozef Lawrynowicz
  0 siblings, 1 reply; 2+ messages in thread
From: Jozef Lawrynowicz @ 2020-10-20 10:05 UTC (permalink / raw)
  To: gnu-gabi

[-- Attachment #1: Type: text/plain, Size: 16258 bytes --]

Hi,

I'd like to propose a new section flag, SHF_GNU_ABIND.

The flag allows the virtual memory address of a section to be specified
within relocatable files, in advance of the final link. When linking
executable files, the linker will try to place sections marked with
SHF_GNU_ABIND at the address specified by their sh_addr field.

"ABIND" is short for "address bind", used to indicate that the
section is "bound" to a specific address at run time.

I've attached a Binutils patch that implements SHF_GNU_ABIND.

I look forward to hearing your thoughts,
Jozef

===========================================================
-----------------------
Section Attribute Flags
-----------------------
+-------------------------------------+
| Name           | Value              |
+-------------------------------------+
| SHF_GNU_ABIND  | 0x400000 (1 << 22) |
+-------------------------------------+

SHF_GNU_ABIND
  The sh_addr field describes the virtual address at which
  the first byte of the section should reside at in memory.

===========================================================

Outline:
  - Motivation
  - Placement using linker script
  - ABI details for SHF_GNU_ABIND runtime initialization
  - Implementation
  - Alternative Method to Implement the "location" attribute
  - Conclusion
  - Appendix - Examples

----------
Motivation
----------
Placement of function or variable declarations at specific addresses can
be useful in a variety of situations.

Many processors have areas of the memory map with special properties.
The data stored in these areas may be treated by the hardware in
some unique way.
For example:
- Slots in an interrupt vector table
  When an interrupt triggers, the processor will read the address of the
  associated interrupt service routine from a particular address within
  the vector table.
- Memory-mapped peripheral registers
  To check the status, or modify the behavior, of peripherals, control
  registers are defined at specific addresses and can be read from, or
  written to.

Programmers may also find it useful to store information at a
memorable address. Perhaps some data is shared between a bootloader and
the application itself, stored in a common area of memory.

The requirement for SHF_GNU_ABIND is motivated by a broader proposal for
a new attribute, "location", which can be set on function and variable
declarations in the source code.
This attribute will enable a declaration to be placed at a specific
virtual address, and SHF_GNU_ABIND is used to convey this requirement in
relocatable files.

Similar attributes are implemented in other toolchains, such as ARM
Keil's "at" attribute, and the "location" attribute implemented in TI
toolchains across a variety of their processors.

-----------------------------
Placement using linker script
-----------------------------
To place a declaration of a function or variable at a specific address,
the programmer must currently make modifications to the linker script.
Linker script modifications for this are cumbersome, and must be coupled
with modifications to the source code that place the declaration in a
specific section.

The programmer has to:
- Apply the "section" attribute to the declaration they want to place
  at a specific address, so it gets placed in a uniquely named section.
- Modify the linker script, creating a new output section with
  the desired VMA for the declaration, containing a rule to match the
  section they created for the declaration in the source code.
 
However, there are some potential pitfalls the programmer can fall into
when making linker script modifications to place their section at
a specific address:
- The programmer may not consider the best position to put their new
  output section within the list of output sections.
  In a linker script with multiple output sections, a sub-optimal
  decision about which sections they will place their new section
  between could result in large, empty gaps in their executable file.
  This limits the space available for their application, possibly
  resulting in the program failing to link, or heap/stack availability
  being low.
  * With SHF_GNU_ABIND, the optimal position of the new output section
    within the statement list can be automatically calculated.
- The programmer may not consider whether an LMA region needs to be set
  for their new output section, if their desired address is within a
  non-loadable memory region.
  * With SHF_GNU_ABIND, the linker automatically decides if a separate
    LMA region is required and sets the load address accordingly.

There are additional generic benefits to avoiding linker script
modifications:
- The requirement for placement of a declaration at a specific address
  may be application-specific, so consolidating this requirement to be
  contained entirely within the source code improves portability.
- Generally avoiding linker script modifications can improve the user
  experience, when the programmer is inexperienced with the linker
  script format.  The boilerplate linker script code required for
  standard application operation can make modifications error-prone and
  have unintended side-effects.

Furthermore, with SHF_GNU_ABIND, the number of individual changes
required to achieve placement at a specific address is reduced.

With linker script modifications:
  *.ld:
| SECTIONS
| {
|   ... other sections ...
|   my_decl 0x1000 : { *(.data.my_decl_at_0x1000) }
|   ... other sections ...
| }
  *.c:
| int __attribute__((section(".data.my_decl_at_0x1000"))) = 0xABCD;

With SHF_GNU_ABIND and the "location" attribute:
  *.c:
| int __attribute__((location(0x1000))) = 0xABCD;

Following are some further ABI details, describing the implementation
required to support placement of sections with LMA not equal to VMA, and
sections consisting of zero-initialized data.

================================================================
----------------
Special Sections
----------------
.gnu.abind_init_array
  This section holds an array of ElfXX_AbindInitArray_Entry entries,
  which describe how to initialize the contents of SHF_GNU_ABIND
  sections at runtime, for sections that require runtime initialization.
  See "SHF_GNU_ABIND Section Initialization" for more details.

------------------------------------
SHF_GNU_ABIND Section Initialization
------------------------------------
Some SHF_GNU_ABIND sections will require initialization at runtime. The
conditions for the initialization requirement are:
- The program will run on a ROM based system, so SHF_GNU_ABIND sections
  with their virtual memory address (VMA) specified in a non-loadable
  memory region require their contents to be copied from their load
  memory address (LMA) to their VMA during program initialization.
- The SHF_GNU_ABIND section has type SHT_NOBITS and would have been
  placed in a .bss output section, and its contents set to zero during
  program initialization. The specified VMA for the SHF_GNU_ABIND
  section may not be within the bounds of the zero-initialized block of
  other .bss sections, so the section will require it's contents to be
  explicitly set to zero at runtime.  For targets that do not zero .bss,
  this is not required.

For devices without non-loadable memory regions, and so will not have
any sections with LMA not equal to VMA, it is possible to avoid the need
for .gnu.abind_init_array.
Defining any SHF_GNU_ABIND sections containing zero-initialized data
explicitly as an SHT_PROGBITS ".data" section and initializing its
contents to 0 will ensure the program loader sets its contents to 0,
rather than relying on the application to zero it.
.gnu.abind_init_array will therefore not be required in this case.
If the compiler handles this automatically, .gnu.abind_init_array
support could be entirely omitted for the target.

The "__run_gnu_abind_init_array" function performs runtime
initialization of SHF_GNU_ABIND sections that require it, as described
by .gnu.abind_init_array entries.

If .gnu.abind_init_array will be present in the executable file and has
non-zero size, an entry for __run_gnu_abind_init_array must be placed in
the ".init_array" section.

-----------------------------
.gnu.abind_init_array Entries
-----------------------------
typedef struct
{
  Elf32_Addr vma;
  Elf32_Addr lma;
  Elf32_Word size;
} Elf32_AbindInitArray_Entry;

typedef struct
{
  Elf64_Addr vma;
  Elf64_Addr lma;
  Elf64_Xword size;
} Elf64_AbindInitArray_Entry;

The bounds of .gnu.abind_init_array are described by the following
symbols:
  __gnu_abind_init_array_start
  __gnu_abind_init_array_end

To initialize the sections, __run_gnu_abind_init_array has a choice for
each entry:
- When lma != vma, copy "size" bytes from address "lma" to address
  "vma".
- When lma == vma, this is an SHT_NOBITS section requiring
  zero-initialization. Set "size" bytes starting from address "vma" to
  0.
    
A sample implementation of __run_gnu_abind_init_array for a 32-bit
target is shown below:

  void
  __run_gnu_abind_init_array (void)
  {
    void *ptr;
    for (ptr = &__gnu_abind_init_array_start;
         ptr !=  &__gnu_abind_init_array_end;
         ptr += sizeof (Elf32_AbindInitArray_Entry))
      {
        Elf32_AbindInitArray_Entry *ent = ptr;
         
        if (ent->lma != ent->vma)
          __builtin_memcpy (ent->vma, ent->lma, ent->size);
        else
          __builtin_memset (ent->vma, 0, ent->size);
      }
  }
================================================================
--------------
Implementation
--------------
GCC supports a new "location" attribute, which can be applied to
function and variable declarations. The argument to the "location"
attribute indicates the virtual memory address that the declaration
should be placed at.

  int __attribute__((location(0x5000))) checksum = 0xABCD;

GCC will place the declaration that the "location" attribute is applied
to in its own, uniquely named section. GCC will then describe the
requirement for placement at a specific address to the assembler.

For systems which do not have any non-loadable memory regions, GCC can
modify the type and name created for zero-initialized data. Placing this
data in an SHT_PROGBITS section with the ".data" prefix, instead of an
SHT_NOBITS sections with the ".bss" prefix, and explicitly initializing
its contents to 0 will avoid the need for any runtime support for
SHF_GNU_ABIND via .gnu.abind_init_array.

GAS supports a new flag value "A" to the "flags" argument of the
.section directive. The specified address is read from the
"flag_specific_arguments" argument to .section.

  .section .data.checksum,"awA",0x5000

GAS will set the SHF_GNU_ABIND flag on the section, and the sh_addr
field to the specified address.

When loading object files to link an executable file, LD will "orphan"
SHF_GNU_ABIND sections by renaming them, giving them the ".gnu.abind"
prefix. This means they won't match any input section rules from the
linker script. The "orphaned" SHF_GNU_ABIND input sections will now have
their own output sections, which can be placed freely at the address
specified by the sh_addr field of the corresponding input section.

--------------------------------------------------------
Alternative Method to Implement the "location" attribute
--------------------------------------------------------
An alternative method of conveying the requirement for placement of a
section at a specific address, from the compiler to the linker, would be
for the compiler to set a specific section name which captures both the
status of the section as one requiring placement, and the address it
should be placed at.

For example:
  int __attribute__((location(0x5000))) checksum = 0xABCD;
becomes
  .section .gnu_location_0x5000.data.checksum,"aw"

In theory, the assembler and linker implementation does not need to be
very different from what is required for SHF_GNU_ABIND, as these
programs can set an internal flag on sections which have the
.gnu_location prefix to treat them in a special way.

However, the additional prefix for the section on entry to the assembler
and linker might interfere with some common behavior.

It is also not standard to convey information about a section
in the name.  This method should only be used if the proposed ABI
modifications cannot be made for some reason.
Additionally, it would result in the loss of opportunity for
standardization of .gnu.abind_init_array and associated runtime
functionality.

----------
Conclusion
----------
The "location" attribute, making use of SHF_GNU_ABIND, will improve the
user experience and simplify development for programmers wishing to make
use of the functionality to place function and variable declarations at
specific addresses. The behavior can now be implemented in fewer steps,
and without leaving the source code. Cumbersome and error-prone linker
script modifications are avoided.

For embedded devices, maintenance of board support packages is eased, as
many hardware features that had to be described in both header file and
linker scripts, with the corresponding definitions requiring precise
alignment between the files, now require specification only in header
files.

Applications are more portable, as those which require declarations to
be placed at a specific address can now be entirely described from the
source code, without requiring an accompanying linker script.

-------------------
Appendix - Examples
-------------------
Below are some examples for the "location" attribute in real use cases.

Mapping a Structure over Memory-mapped Peripheral Registers
---------------------------------------------------------
Current methods for accessing memory-mapped registers are:
- Set the address of a structure at runtime, in application code.
  * Requires additional code to setup the address of the structure
    before registers can be accessed.
- Use a symbolic reference to the registers, defined in header files but
  resolved at link time.
  * Requires implementation in both header file and linker scripts.

The "location" attribute allows consolidation of this information
entirely within the source code, and without any runtime initialization
required.

  typedef struct
  {
    char regA;
    char regB;
    char ctrl;
  } peripheral_reg;

  peripheral_reg __attribute__((location(0x65000))) pregs1;

  /* Above code can be hidden in a header file from a board support
     package for a particular device.  */

  void
  manip_regs (void)
  {
    pregs1.ctrl = 1;        /* MOV  #1, &0x65002 */
    pregs1.regA = 10;       /* MOV  #10, &0x65000 */
    pregs1.regB = 20;       /* MOV  #20, &0x65001 */
    pregs1.ctrl = 0;        /* MOV  #0, &0x65002 */
  }

Placement in the Interrupt Vector Table
---------------------------------------
Implementation of interrupt vectors currently requires explicit support
between the source code and linker script:

  SECTIONS
  {
    __ivec_rtc 0xFF00 : { KEEP (*(__ivec_rtc)) }
  }

  /* "interrupt" attribute is required to create the "__ivec_rtc"
     section and place the address of "isr_rtc" within it.  */
  void __attribute__((interrupt("rtc"))
  isr_rtc (void)
  {
    clock_var++;
  }

The name of the interrupt service routine must be aligned between the
source code, compiler (a hard-coded prefix must be added) and the linker
script.

The "location" attribute, used in conjunction with "retain" greatly
simplifies the procedure, and does not require any linker script
modifications.

/* If a board support package is available, refer to the address
   symbolically.  */
#ifdef __BSP__
  static void * __attribute__((retain,location(VEC_RTC)))
#else
  static void * __attribute__((retain,location(0xFF00)))
#endif
    __ivec_rtc = isr_rtc;

  void
  isr_rtc (void)
  {
    clock_var++;
  }

As an extension to the proposal, a new "interrupt_loc" attribute could
emit section declarations for vector table slots, setting SHF_GNU_RETAIN
and SHF_GNU_ABIND on the section. This would allow an ISR to be
completely specified with only a function declaration and possibly an
associated definition from a header file.

#ifdef __BSP__
  void __attribute__((interrupt_loc(VEC_RTC)))
#else
  void __attribute__((interrupt_loc(0xFF00)))
#endif
  isr_rtc (void)
  {
    clock_var++;
  }

[-- Attachment #2: 0001-Implement-SHF_GNU_ABIND.patch --]
[-- Type: text/plain, Size: 90051 bytes --]

From 5ed0fbad2b15c1a0419c4046fc11488811c68152 Mon Sep 17 00:00:00 2001
From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
Date: Tue, 20 Oct 2020 10:44:37 +0100
Subject: [PATCH] Implement SHF_GNU_ABIND

---
 bfd/elf-bfd.h                             |  10 +-
 bfd/elf.c                                 |   9 +-
 bfd/elflink.c                             | 133 ++++-
 binutils/readelf.c                        |  10 +
 gas/config/obj-elf.c                      |  47 +-
 gas/config/obj-elf.h                      |   1 +
 gas/testsuite/gas/elf/section10.d         |   4 +-
 gas/testsuite/gas/elf/section10.s         |   4 +-
 include/elf/common.h                      |   2 +
 include/elf/external.h                    |  14 +
 ld/configure.tgt                          |   1 -
 ld/ldelfgen.c                             | 568 ++++++++++++++++++++++
 ld/ldlang.c                               |   8 +-
 ld/ldlang.h                               |   5 +
 ld/testsuite/ld-arm/abind-1.d             |  49 ++
 ld/testsuite/ld-arm/abind-1.s             | 183 +++++++
 ld/testsuite/ld-arm/arm-elf.exp           |   2 +
 ld/testsuite/ld-elf/abind-1.s             |  57 +++
 ld/testsuite/ld-elf/abind-1a.d            |  27 +
 ld/testsuite/ld-elf/abind-1a.ld           |  28 ++
 ld/testsuite/ld-elf/abind-1b.d            |  27 +
 ld/testsuite/ld-elf/abind-1b.ld           |  28 ++
 ld/testsuite/ld-elf/abind-1c.d            |  27 +
 ld/testsuite/ld-elf/abind-1c.ld           |  28 ++
 ld/testsuite/ld-elf/abind-1d.d            |  27 +
 ld/testsuite/ld-elf/abind-1d.ld           |  28 ++
 ld/testsuite/ld-elf/abind-1e.d            |  27 +
 ld/testsuite/ld-elf/abind-1e.ld           |  27 +
 ld/testsuite/ld-elf/abind-1f.d            |  27 +
 ld/testsuite/ld-elf/abind-1f.ld           |  28 ++
 ld/testsuite/ld-elf/abind-1g.d            |  27 +
 ld/testsuite/ld-elf/abind-1g.ld           |  27 +
 ld/testsuite/ld-elf/abind-1h.d            |  27 +
 ld/testsuite/ld-elf/abind-1h.ld           |  27 +
 ld/testsuite/ld-elf/abind-1i.d            |  27 +
 ld/testsuite/ld-elf/abind-1i.ld           |  22 +
 ld/testsuite/ld-elf/abind-2.s             | 183 +++++++
 ld/testsuite/ld-elf/abind-2a.d            |  73 +++
 ld/testsuite/ld-elf/abind-2a.ld           |  28 ++
 ld/testsuite/ld-elf/abind-2b.d            |  73 +++
 ld/testsuite/ld-elf/abind-2b.ld           |  28 ++
 ld/testsuite/ld-elf/abind-2c.d            |  73 +++
 ld/testsuite/ld-elf/abind-2c.ld           |  27 +
 ld/testsuite/ld-elf/abind-2d.d            |  73 +++
 ld/testsuite/ld-elf/abind-2d.ld           |  28 ++
 ld/testsuite/ld-elf/abind-2e.d            |  73 +++
 ld/testsuite/ld-elf/abind-2e.ld           |  27 +
 ld/testsuite/ld-elf/abind-2f.d            |  73 +++
 ld/testsuite/ld-elf/abind-2f.ld           |  27 +
 ld/testsuite/ld-elf/abind-2g.d            |  73 +++
 ld/testsuite/ld-elf/abind-2g.ld           |  22 +
 ld/testsuite/ld-msp430-elf/location-1.d   |  23 +
 ld/testsuite/ld-msp430-elf/location-1.s   |  98 ++++
 ld/testsuite/ld-msp430-elf/location-2.d   |  30 ++
 ld/testsuite/ld-msp430-elf/location-2.s   |  76 +++
 ld/testsuite/ld-msp430-elf/location-3.d   |  10 +
 ld/testsuite/ld-msp430-elf/location-3.s   |  12 +
 ld/testsuite/ld-msp430-elf/msp430-elf.exp |   4 +
 ld/testsuite/ld-x86-64/abind-1.d          |  54 ++
 ld/testsuite/ld-x86-64/abind-1.s          | 175 +++++++
 ld/testsuite/ld-x86-64/x86-64.exp         |   1 +
 61 files changed, 2938 insertions(+), 19 deletions(-)
 create mode 100644 ld/testsuite/ld-arm/abind-1.d
 create mode 100644 ld/testsuite/ld-arm/abind-1.s
 create mode 100644 ld/testsuite/ld-elf/abind-1.s
 create mode 100644 ld/testsuite/ld-elf/abind-1a.d
 create mode 100644 ld/testsuite/ld-elf/abind-1a.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1b.d
 create mode 100644 ld/testsuite/ld-elf/abind-1b.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1c.d
 create mode 100644 ld/testsuite/ld-elf/abind-1c.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1d.d
 create mode 100644 ld/testsuite/ld-elf/abind-1d.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1e.d
 create mode 100644 ld/testsuite/ld-elf/abind-1e.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1f.d
 create mode 100644 ld/testsuite/ld-elf/abind-1f.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1g.d
 create mode 100644 ld/testsuite/ld-elf/abind-1g.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1h.d
 create mode 100644 ld/testsuite/ld-elf/abind-1h.ld
 create mode 100644 ld/testsuite/ld-elf/abind-1i.d
 create mode 100644 ld/testsuite/ld-elf/abind-1i.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2.s
 create mode 100644 ld/testsuite/ld-elf/abind-2a.d
 create mode 100644 ld/testsuite/ld-elf/abind-2a.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2b.d
 create mode 100644 ld/testsuite/ld-elf/abind-2b.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2c.d
 create mode 100644 ld/testsuite/ld-elf/abind-2c.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2d.d
 create mode 100644 ld/testsuite/ld-elf/abind-2d.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2e.d
 create mode 100644 ld/testsuite/ld-elf/abind-2e.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2f.d
 create mode 100644 ld/testsuite/ld-elf/abind-2f.ld
 create mode 100644 ld/testsuite/ld-elf/abind-2g.d
 create mode 100644 ld/testsuite/ld-elf/abind-2g.ld
 create mode 100644 ld/testsuite/ld-msp430-elf/location-1.d
 create mode 100644 ld/testsuite/ld-msp430-elf/location-1.s
 create mode 100644 ld/testsuite/ld-msp430-elf/location-2.d
 create mode 100644 ld/testsuite/ld-msp430-elf/location-2.s
 create mode 100644 ld/testsuite/ld-msp430-elf/location-3.d
 create mode 100644 ld/testsuite/ld-msp430-elf/location-3.s
 create mode 100644 ld/testsuite/ld-x86-64/abind-1.d
 create mode 100644 ld/testsuite/ld-x86-64/abind-1.s

diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
index ffb75f7919..1dc08ba2ba 100644
--- a/bfd/elf-bfd.h
+++ b/bfd/elf-bfd.h
@@ -1897,15 +1897,17 @@ struct output_elf_obj_tdata
   bfd_boolean flags_init;
 };
 
-/* Indicate if the bfd contains SHF_GNU_MBIND/SHF_GNU_RETAIN sections or
-   symbols that have the STT_GNU_IFUNC symbol type or STB_GNU_UNIQUE
-   binding.  Used to set the osabi field in the ELF header structure.  */
+/* Indicate if the bfd contains SHF_GNU_MBIND, SHF_GNU_RETAIN or SHF_GNU_ABIND
+   sections or symbols that have the STT_GNU_IFUNC symbol type or
+   STB_GNU_UNIQUE binding.  Used to set the osabi field in the ELF header
+   structure.  */
 enum elf_gnu_osabi
 {
   elf_gnu_osabi_mbind = 1 << 0,
   elf_gnu_osabi_ifunc = 1 << 1,
   elf_gnu_osabi_unique = 1 << 2,
   elf_gnu_osabi_retain = 1 << 3,
+  elf_gnu_osabi_abind = 1 << 4,
 };
 
 typedef struct elf_section_list
@@ -2035,7 +2037,7 @@ struct elf_obj_tdata
   ENUM_BITFIELD (dynamic_lib_link_class) dyn_lib_class : 4;
 
   /* Whether the bfd uses OS specific bits that require ELFOSABI_GNU.  */
-  ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 4;
+  ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 5;
 
   /* Whether if the bfd contains the GNU_PROPERTY_NO_COPY_ON_PROTECTED
      property.  */
diff --git a/bfd/elf.c b/bfd/elf.c
index dc097e825a..5e30b1016a 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -3284,8 +3284,13 @@ elf_fake_sections (bfd *abfd, asection *asect, void *fsarg)
 
   /* Don't clear sh_flags. Assembler may set additional bits.  */
 
-  if ((asect->flags & SEC_ALLOC) != 0
-      || asect->user_set_vma)
+  if (!arg->link_info
+      && elf_section_flags (asect) & SHF_GNU_ABIND)
+    {
+      /* Don't clobber sh_addr already set for a GNU_ABIND section.  */
+    }
+  else if ((asect->flags & SEC_ALLOC) != 0
+	   || asect->user_set_vma)
     this_hdr->sh_addr = asect->vma * bfd_octets_per_byte (abfd, asect);
   else
     this_hdr->sh_addr = 0;
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 742254055c..d93e744cf1 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -4044,6 +4044,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
   size_t tabsize = 0;
   asection *s;
   bfd_boolean just_syms;
+  static bfd_boolean created_abind_init_sec = FALSE;
 
   htab = elf_hash_table (info);
   bed = get_elf_backend_data (abfd);
@@ -4081,16 +4082,16 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       (_("alternate ELF machine code found (%d) in %pB, expecting %d"),
        ehdr->e_machine, abfd, bed->elf_machine_code);
 
-  /* As a GNU extension, any input sections which are named
-     .gnu.warning.SYMBOL are treated as warning symbols for the given
-     symbol.  This differs from .gnu.warning sections, which generate
-     warnings when they are included in an output file.  */
-  /* PR 12761: Also generate this warning when building shared libraries.  */
   for (s = abfd->sections; s != NULL; s = s->next)
     {
       const char *name;
 
       name = bfd_section_name (s);
+      /* As a GNU extension, any input sections which are named
+	 .gnu.warning.SYMBOL are treated as warning symbols for the given
+	 symbol.  This differs from .gnu.warning sections, which generate
+	 warnings when they are included in an output file.  */
+      /* PR 12761: Also generate this warning when building shared libraries.  */
       if (CONST_STRNEQ (name, ".gnu.warning."))
 	{
 	  char *msg;
@@ -4146,6 +4147,65 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 	      s->flags |= SEC_EXCLUDE;
 	    }
 	}
+      else if (!bfd_link_relocatable (info)
+	       && (elf_section_data (s)->this_hdr.sh_flags & SHF_GNU_ABIND))
+	{
+	  /* Orphan sections with the SHF_GNU_ABIND flag so they can be freely
+	     moved around the statement list, by renaming them so they don't
+	     match a linker script rule.  */
+	  bfd_rename_section (s, concat (".gnu.abind", s->name, (const char *)NULL));
+
+	  if (!created_abind_init_sec)
+	    {
+	      /* Create .gnu.abind_init_array and an undefined symbol for the
+		 library function required to make use of it.  */
+	      asection *s_init;
+	      flagword flags = (SEC_HAS_CONTENTS | SEC_READONLY
+				| SEC_ALLOC | SEC_LOAD | SEC_KEEP);
+
+	      s_init = bfd_make_section_anyway_with_flags (abfd, ".gnu.abind_init_array", flags);
+	      if (s_init == NULL)
+		{
+		  _bfd_error_handler
+		    (_("%pB: could not create .gnu.abind_init_array section\n"), abfd);
+		  goto error_return;
+		}
+
+	      if (bed->s->arch_size == 64)
+		{
+		  elf_section_data (s_init)->this_hdr.sh_entsize
+		    = sizeof (Elf64_External_AbindInitArray_Entry);
+		  bfd_set_section_alignment (s_init, 8);
+		}
+	      else
+		{
+		  elf_section_data (s_init)->this_hdr.sh_entsize
+		    = sizeof (Elf32_External_AbindInitArray_Entry);
+		  bfd_set_section_alignment (s_init, 4);
+		}
+
+	      /* Create an undefined symbol to ............. */
+	      struct bfd_link_hash_entry *h;
+
+	      h = bfd_link_hash_lookup (info->hash, "__run_gnu_abind_init_array", TRUE, FALSE, TRUE);
+	      if (h == NULL)
+		{
+		  _bfd_error_handler
+		    (_("%pB: could not create __run_gnu_abind_init_array symbol\n"), abfd);
+		  goto error_return;
+		}
+	      if (h->type == bfd_link_hash_new)
+		{
+		  h->type = bfd_link_hash_undefined;
+		  h->u.undef.abfd = NULL;
+		  h->non_ir_ref_regular = TRUE;
+		  if (is_elf_hash_table (info->hash))
+		    ((struct elf_link_hash_entry *) h)->mark = 1;
+		  bfd_link_add_undef (info->hash, h);
+		}
+	      created_abind_init_sec = TRUE;
+	    }
+	}
     }
 
   just_syms = ((s = abfd->sections) != NULL
@@ -9801,6 +9861,66 @@ elf_link_output_symstrtab (struct elf_final_link_info *flinfo,
   return 1;
 }
 
+static bfd_boolean
+_elf_link_swap_out_abind_init_array (bfd *abfd)
+{
+  asection *init_sec, *s;
+  bfd_vma pos;
+  const struct elf_backend_data *bed;
+  int sizeof_addr;
+
+  init_sec = bfd_get_section_by_name (abfd, ".gnu.abind_init_array");
+  if (init_sec == NULL)
+    return TRUE;
+
+  bed = get_elf_backend_data (abfd);
+
+  if (bed->s->arch_size == 64)
+    {
+      elf_section_data (init_sec)->this_hdr.sh_entsize
+	= sizeof (Elf64_External_AbindInitArray_Entry);
+      sizeof_addr = 8;
+    }
+  else
+    {
+      elf_section_data (init_sec)->this_hdr.sh_entsize
+	= sizeof (Elf32_External_AbindInitArray_Entry);
+      sizeof_addr = 4;
+    }
+
+  bfd_byte *data_contents = bfd_malloc (init_sec->size);
+  memset (data_contents, 0, init_sec->size);
+  pos = 0;
+
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if ((elf_section_flags (s) & SHF_GNU_ABIND) == 0
+	  /* .bss sections and those with LMA != VMA will require runtime
+	     initialization using .gnu.abind_init_array.  */
+	  || !(s->lma != s->vma
+	       || (s->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
+	continue;
+
+      if (pos >= init_sec->size)
+	{
+	  _bfd_error_handler(_("%pB: .gnu.abind_init_array has incorrect size\n"), abfd);
+	  return FALSE;
+	}
+
+      /* FIXME Use a "swap_out" function to write out the entry.  */
+      /* When LMA == VMA, __run_gnu_abind_init_array knows the section is for
+	 .bss and will instead zero-initialize for the given size.  */
+      memcpy (data_contents + pos, &s->vma, sizeof_addr);
+      pos += sizeof_addr;
+      memcpy (data_contents + pos, &s->lma, sizeof_addr);
+      pos += sizeof_addr;
+      memcpy (data_contents + pos, &s->size, sizeof_addr);
+      pos += sizeof_addr;
+    }
+  bfd_set_section_contents (abfd, init_sec, data_contents, 0, init_sec->size);
+  return TRUE;
+}
+
 /* Swap symbols out to the symbol table and flush the output symbols to
    the file.  */
 
@@ -12823,6 +12943,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
       goto return_local_hash_table;
     }
 
+  /* Swap out .gnu.abind_init_array.  */
+  _elf_link_swap_out_abind_init_array (abfd);
+
   /* Now we know the size of the symtab section.  */
   if (bfd_get_symcount (abfd) > 0)
     {
diff --git a/binutils/readelf.c b/binutils/readelf.c
index e6ec99a2cc..f43687c1ed 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -5979,6 +5979,8 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
       /* 25 */ { STRING_COMMA_LEN ("VLE") },
       /* GNU specific.  */
       /* 26 */ { STRING_COMMA_LEN ("GNU_RETAIN") },
+      /* GNU specific.  */
+      /* 27 */ { STRING_COMMA_LEN ("GNU_ABIND") },
     };
 
   if (do_section_details)
@@ -6070,6 +6072,8 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
 		case ELFOSABI_FREEBSD:
 		  if (flag == SHF_GNU_RETAIN)
 		    sindex = 26;
+		  else if (flag == SHF_GNU_ABIND)
+		    sindex = 27;
 		  /* Fall through */
 		case ELFOSABI_NONE:
 		  if (flag == SHF_GNU_MBIND)
@@ -6148,6 +6152,12 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
 			  *p = 'R';
 			  break;
 			}
+		      else if (flag == SHF_GNU_ABIND)
+			{
+			  *p = 'a';
+			  break;
+			}
+
 		      /* Fall through */
 		    case ELFOSABI_NONE:
 		      if (flag == SHF_GNU_MBIND)
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
index 6586478975..2597b1018a 100644
--- a/gas/config/obj-elf.c
+++ b/gas/config/obj-elf.c
@@ -741,6 +741,7 @@ obj_elf_change_section (const char *name,
       elf_section_type (sec) = type;
       elf_section_flags (sec) = attr;
       elf_section_data (sec)->this_hdr.sh_info = match_p->sh_info;
+      elf_section_data (sec)->this_hdr.sh_addr = match_p->sh_addr;
 
       /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
       if (type == SHT_NOBITS)
@@ -801,7 +802,14 @@ obj_elf_change_section (const char *name,
 		  | SEC_THREAD_LOCAL))
 	      || ((elf_tdata (stdoutput)->has_gnu_osabi & elf_gnu_osabi_retain)
 		  && ((elf_section_flags (old_sec) ^ match_p->sh_flags)
-		      & SHF_GNU_RETAIN)))
+		      & SHF_GNU_RETAIN))
+	      /* Check GNU_ABIND state matches, and the address are the
+		 same.  */
+	      || ((elf_tdata (stdoutput)->has_gnu_osabi & elf_gnu_osabi_abind)
+		  && (((elf_section_flags (old_sec) ^ match_p->sh_flags)
+		      & SHF_GNU_ABIND)
+		      || ((elf_section_data (old_sec)->this_hdr.sh_addr
+			   != match_p->sh_addr)))))
 	    {
 	      if (ssect != NULL)
 		as_warn (_("ignoring changed section attributes for %s"), name);
@@ -867,6 +875,9 @@ obj_elf_parse_section_letters (char *str, size_t len,
 	case 'R':
 	  *gnu_attr |= SHF_GNU_RETAIN;
 	  break;
+	case 'A':
+	  *gnu_attr |= SHF_GNU_ABIND;
+	  break;
 	case '?':
 	  *is_clone = TRUE;
 	  break;
@@ -1332,6 +1343,36 @@ obj_elf_section (int push)
 	  if ((gnu_attr & SHF_GNU_RETAIN) != 0)
 	    match.sh_flags |= SHF_GNU_RETAIN;
 
+	  if ((gnu_attr & SHF_GNU_ABIND) != 0 && *input_line_pointer == ',')
+	    {
+	      char *save = input_line_pointer;
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      if (ISDIGIT (* input_line_pointer))
+		{
+		  char *t = input_line_pointer;
+		  match.sh_addr = strtoul (input_line_pointer,
+					   &input_line_pointer, 0);
+		  if (match.sh_addr == (unsigned int) -1)
+		    {
+		      as_warn (_("invalid address for GNU_ABIND section: %s"), t);
+		      match.sh_addr = 0;
+		    }
+		}
+	      else
+		{
+		  as_warn (_("expected integer constant for GNU_ABIND section address"));
+		  match.sh_addr = 0;
+		  input_line_pointer = save;
+		}
+	    }
+	  else if ((attr & SHF_GNU_ABIND) != 0)
+	    {
+	      as_warn (_("address for SHF_GNU_ABIND not specified"));
+	      attr &= ~SHF_GNU_ABIND;
+	    }
+
+
 	  if (*input_line_pointer == ',')
 	    {
 	      char *save = input_line_pointer;
@@ -1420,7 +1461,7 @@ obj_elf_section (int push)
  done:
   demand_empty_rest_of_line ();
 
-  if ((gnu_attr & (SHF_GNU_MBIND | SHF_GNU_RETAIN)) != 0)
+  if ((gnu_attr & SHF_MASKOS) != 0)
     {
       struct elf_backend_data *bed;
       bfd_boolean mbind_p = (gnu_attr & SHF_GNU_MBIND) != 0;
@@ -1446,6 +1487,8 @@ obj_elf_section (int push)
 	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind;
 	  if ((gnu_attr & SHF_GNU_RETAIN) != 0)
 	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_retain;
+	  if ((gnu_attr & SHF_GNU_ABIND) != 0)
+	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_abind;
 
 	  attr |= gnu_attr;
 	}
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 0a91ed462f..c5492cc8bc 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -108,6 +108,7 @@ struct elf_section_match
   const char *   linked_to_symbol_name;
   unsigned int   section_id;
   unsigned int   sh_info;		/* ELF section information.  */
+  bfd_vma	 sh_addr;		/* ELF section address.  */
   bfd_vma	 sh_flags;		/* ELF section flags.  */
   flagword       flags;
 };
diff --git a/gas/testsuite/gas/elf/section10.d b/gas/testsuite/gas/elf/section10.d
index 6aa7b088b1..8346d7b13c 100644
--- a/gas/testsuite/gas/elf/section10.d
+++ b/gas/testsuite/gas/elf/section10.d
@@ -18,7 +18,7 @@
 #...
 [ 	]*\[.*\][ 	]+sec3
 [ 	]*PROGBITS.*
-[ 	]*\[.*fedff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
+[ 	]*\[.*fe9ff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*e900000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
 #...
 [ 	]*\[.*\][ 	]+sec4
 [ 	]*LOOS\+0x11[ 	].*
@@ -26,7 +26,7 @@
 #...
 [ 	]*\[.*\][ 	]+sec5
 [ 	]*LOUSER\+0x9[ 	].*
-[ 	]*\[.*fedf0000\]:.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
+[ 	]*\[.*fe9f0000\]:.* EXCLUDE, OS \(.*e900000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
 [ 	]*\[.*\][ 	]+.data.foo
 [ 	]*LOUSER\+0x7f000000[ 	].*
 [ 	]*\[0+003\]: WRITE, ALLOC
diff --git a/gas/testsuite/gas/elf/section10.s b/gas/testsuite/gas/elf/section10.s
index d52b3458fb..1236045c3c 100644
--- a/gas/testsuite/gas/elf/section10.s
+++ b/gas/testsuite/gas/elf/section10.s
@@ -7,7 +7,7 @@
 	.word 2
 
 	# Make sure that specifying further arguments to .sections is still supported
-	.section sec3, "0xfedff000MS", %progbits, 32
+	.section sec3, "0xfe9ff000MS", %progbits, 32
 	.word 3
 
 	# Make sure that extra flags can be set for well known sections as well.
@@ -19,7 +19,7 @@
 	.word 5
 
 	# Test both together, with a quoted type value.
-	.section sec5, "0xfedf0000", "0x80000009"
+	.section sec5, "0xfe9f0000", "0x80000009"
 	.word 6
 
 	# Test that declaring an extended version of a known special section works.
diff --git a/include/elf/common.h b/include/elf/common.h
index c01e562c78..babff115d9 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -555,6 +555,8 @@
 #define SHF_MASKOS	0x0FF00000	/* New value, Oct 4, 1999 Draft */
 #define SHF_GNU_BUILD_NOTE    (1 << 20)	/* Section contains GNU BUILD ATTRIBUTE notes.  */
 #define SHF_GNU_RETAIN	      (1 << 21)	/* Section should not be garbage collected by the linker.  */
+#define SHF_GNU_ABIND	      (1 << 22)	/* Section should placed at a specific VMA.  */
+
 #define SHF_MASKPROC	0xF0000000	/* Processor-specific semantics */
 
 /* This used to be implemented as a processor specific section flag.
diff --git a/include/elf/external.h b/include/elf/external.h
index 230fdabd87..6f6e678dcd 100644
--- a/include/elf/external.h
+++ b/include/elf/external.h
@@ -311,6 +311,20 @@ typedef struct
   unsigned char		a_val[8];
 } Elf64_External_Auxv;
 
+typedef struct
+{
+  unsigned char vma[4];
+  unsigned char lma[4];
+  unsigned char size[4];
+} Elf32_External_AbindInitArray_Entry;
+
+typedef struct
+{
+  unsigned char vma[8];
+  unsigned char lma[8];
+  unsigned char size[8];
+} Elf64_External_AbindInitArray_Entry;
+
 /* Size of SHT_GROUP section entry.  */
 
 #define GRP_ENTRY_SIZE		4
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 70359301b5..9db64ed12d 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -584,7 +584,6 @@ moxie-*-*)		targ_emul=elf32moxie
 			;;
 msp430-*-*)		targ_emul=msp430elf
 			targ_extra_emuls="msp430X"
-			targ_extra_ofiles=ldelfgen.o
 			;;
 mt-*elf)		targ_emul=elf32mt
 			targ_extra_ofiles=ldelfgen.o
diff --git a/ld/ldelfgen.c b/ld/ldelfgen.c
index 3a5619435c..c1b9f8c1ab 100644
--- a/ld/ldelfgen.c
+++ b/ld/ldelfgen.c
@@ -30,11 +30,18 @@
 #include "elf-bfd.h"
 #include "ldelfgen.h"
 
+static bfd_boolean place_bound_sections (void);
+static void dump_sections (lang_statement_list_type *root, lang_memory_region_type *r, int indent);
+
+
 void
 ldelf_map_segments (bfd_boolean need_layout)
 {
   int tries = 10;
 
+  if (!bfd_link_relocatable (&link_info))
+    place_bound_sections ();
+
   do
     {
       lang_relax_sections (need_layout);
@@ -211,3 +218,564 @@ extern void ldelf_examine_strtab_for_ctf
    struct elf_strtab_hash *symstrtab ATTRIBUTE_UNUSED)
 {}
 #endif
+
+\f
+/* Start SHF_GNU_ABIND handling.  */
+
+/* Insert __gnu_abind_init_array_{start,end} symbols at the start/end of
+   .gnu.abind_init_array.  */
+static bfd_boolean
+insert_abind_init_array_symbols (void)
+{
+  lang_statement_union_type * curr;
+
+  lang_relax_sections (TRUE);
+
+  for (curr = stat_ptr->head; curr != NULL; curr = curr->header.next)
+    {
+      switch (curr->header.type)
+	{
+	case lang_output_section_statement_enum:
+	  if (curr->output_section_statement.bfd_section
+	      && strcmp (curr->output_section_statement.bfd_section->name,
+			 ".gnu.abind_init_array") == 0)
+	    {
+	      asection *os = curr->output_section_statement.bfd_section;
+	      /* FIXME this gets orphaned at the end of the map file would be
+		 good to attach it for aesthetics.  */
+	      lang_add_assignment (exp_provide ("__gnu_abind_init_array_start",
+						exp_intop (os->vma), FALSE));
+	      lang_add_assignment (exp_provide ("__gnu_abind_init_array_end",
+						exp_intop (os->vma + os->size),
+						FALSE));
+	      return TRUE;
+	    }
+	  break;
+	default:
+	  break;
+	}
+    }
+  return FALSE;
+}
+
+/* Check a memory region exists at the address specified for the GNU_ABIND
+   section, and that we can place the section in that region.  */
+static lang_memory_region_type *
+get_region_for_abind (asection *sec,
+		      bfd_boolean validate_flags ATTRIBUTE_UNUSED)
+{
+  bfd_vma addr = sec->vma;
+  lang_memory_region_type *r;
+  lang_memory_region_type *ret = NULL;
+
+  for (r = get_memory_region_list (); r != NULL; r = r->next)
+    {
+      /* The default memory region spans the entire address space.  Use it if
+	 no other memory region contains the address.  */
+      if (strcmp ("*default*", r->name_list.name) == 0)
+	ret = r;
+      else if (addr >= r->origin && addr < (r->origin + r->length))
+	{
+	  /* FIXME/TODO Check the region we want to place the section in is
+	     compatible with its type.  */
+#if 0
+	  if (validate_flags
+	      && (!verify_region_compatibility (r, stat_ptr, sec->flags)))
+	    {
+	      einfo (_("%P: warning: GNU_ABIND section '%s' is not compatible "
+		       "with the memory region '%s' containing address 0x%v\n"),
+		     sec->name, r->name_list.name, addr);
+	      return NULL;
+	    }
+#endif
+	  return r;
+	}
+    }
+  return ret;
+}
+
+
+/* Detach the output section S from the list that starts from ROOT.  */
+static bfd_boolean
+detach_output_sec (asection *s, lang_statement_list_type *root,
+		   bfd_boolean dump)
+{
+  asection *os;
+  lang_statement_union_type * prev = NULL;
+  lang_statement_union_type * curr;
+  lang_output_section_statement_type * os_stat;
+
+  for (curr = root->head; curr != NULL; curr = curr->header.next)
+    {
+      switch (curr->header.type)
+	{
+	case lang_output_section_statement_enum:
+	  os_stat = &curr->output_section_statement;
+	  if (!os_stat->bfd_section)
+	    break;
+
+	  os = os_stat->bfd_section;
+
+	  if (os == s)
+	    {
+	      /* If this is the last statement, and we are detaching this
+		 statement, we have to point the list tail to the previous
+		 elements' next pointer.  */
+	      if (curr->header.next == NULL)
+		root->tail = &prev->header.next;
+
+	      /* Adjust the next statement pointed to by the previous element.  */
+	      if (prev == NULL)
+		root->head = curr->header.next;
+	      else
+		prev->header.next = curr->header.next;
+
+	      /* This statement is no longer part of any list, so clear the next
+		 element it points to.  */
+	      curr->header.next = NULL;
+
+	      lang_relax_sections (TRUE);
+	      return TRUE;
+	    }
+	  break;
+
+	case lang_group_statement_enum:
+	  if (detach_output_sec (s, &curr->group_statement.children, dump))
+	    return TRUE;
+	  break;
+
+	default:
+	  break;
+	}
+      prev = curr;
+    }
+  return FALSE;
+}
+
+static int
+compare_abind_sec_vma (const void *a, const void *b)
+{
+  asection *asec = *(asection **) a, *bsec = *(asection **) b;
+
+  if (asec->vma > bsec->vma)
+    return 1;
+  else if (asec->vma < bsec->vma)
+    return -1;
+
+  return 0;
+}
+
+/* Create a list of GNU_ABIND sections, sorted in ascending order of
+   their desired VMA.  */
+static unsigned int
+sort_abind_secs (asection ***sec_list_p)
+{
+  bfd *ibfd;
+  asection *sec;
+  asection **sec_list;
+  unsigned int sec_count = 0;
+  unsigned int list_size = 10;
+
+  sec_list = (asection **) xmalloc (list_size * sizeof (asection *));
+  *sec_list_p = sec_list;
+
+  for (ibfd = link_info.input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+    for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+      {
+	if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+	    || !(elf_section_flags (sec) & SHF_GNU_ABIND)
+	    || (elf_section_flags (sec) & SEC_EXCLUDE)
+	    || strcmp (sec->output_section->name, DISCARD_SECTION_NAME) == 0
+	    || sec->output_section == bfd_abs_section_ptr
+	    || (link_info.gc_sections && !sec->gc_mark))
+	  continue;
+
+	/* FIXME add an option to disable placement of lma != vma bound sections.  */
+	/*if (sec->output_section->lma != sec->output_section->vma)*/
+	  /*continue;*/
+
+	/* Before we go any further, validate the address of section.
+	   We may not be able to place it there at all.  */
+	/*if (get_region_for_abind (sec, TRUE) == NULL)*/
+	  /*continue;*/
+
+	if (sec_count == list_size)
+	  {
+	    list_size *= 2;
+	    sec_list = (asection **)
+	      xrealloc (sec_list, list_size * sizeof (asection *));
+	  }
+
+	sec_list[sec_count++] = sec;
+
+	/* Sort the SHF_GNU_ABIND input sections by their specified VMA.  */
+	qsort (sec_list, sec_count, sizeof (asection *), &compare_abind_sec_vma);
+      }
+  return sec_count;
+}
+
+/* Initialize a new lang_statement_union_type with the given output section
+   statement.  */
+static lang_statement_union_type *
+insert_os_stat (lang_output_section_statement_type *os_stat)
+{
+  lang_statement_union_type *new_stmt;
+  new_stmt = stat_alloc (sizeof (lang_output_section_statement_type));
+  new_stmt->header.type = lang_output_section_statement_enum;
+
+  new_stmt->output_section_statement = *os_stat;
+  new_stmt->output_section_statement.addr_tree = NULL;
+  new_stmt->header.next = NULL;
+  return new_stmt;
+}
+
+/* Initialize an output section with a wild statement.  */
+static void
+insert_wild (lang_statement_list_type *list)
+{
+  lang_wild_statement_type *wild = NULL;
+
+  wild = stat_alloc (sizeof (lang_wild_statement_type));
+  wild->header.next = NULL;
+  wild->header.type = lang_wild_statement_enum;
+
+  wild->filename = NULL;
+  wild->filenames_sorted = FALSE;
+  wild->section_flag_list = NULL;
+  wild->exclude_name_list = NULL;
+  wild->section_list = NULL;
+  wild->keep_sections = FALSE;
+  lang_list_init (&wild->children);
+
+  *(list->tail) = (void *)wild;
+  list->tail = &wild->header.next;
+
+  lang_relax_sections (TRUE);
+}
+
+/* Return TRUE if this is the last output section statement in the entire statement list.  */
+static bfd_boolean
+is_last_real_os (lang_statement_union_type *os_stat)
+{
+  lang_statement_union_type *curr;
+  for (curr = os_stat->header.next; curr != NULL; curr = curr->header.next)
+    {
+      switch (curr->header.type)
+	{
+	case lang_output_section_statement_enum:
+	  if (curr->output_section_statement.bfd_section
+	      && (curr->output_section_statement.bfd_section->flags & SEC_ALLOC))
+	    return FALSE;
+	  break;
+	default:
+	  /* FIXME: Do we need to handle group statements?  */
+	  break;
+	}
+    }
+  return TRUE;
+}
+
+
+/* Place SEC before the output section os_stat. If os_stat is NULL, there
+   should be free space at the desired address and we attach it to the
+   statement list as required.  */
+static lang_output_section_statement_type *
+move_abind_os (asection *sec, lang_statement_list_type *root)
+{
+  lang_statement_union_type * curr;
+  lang_statement_union_type * prev = NULL;
+  lang_statement_union_type * prev_os_stat = NULL;
+  lang_statement_union_type * insert_after = NULL;
+  bfd_vma addr = sec->vma;
+  lang_output_section_statement_type * os_stat;
+  asection *os;
+  const int dump = 0;
+  if (dump)
+    printf ("\nMoving %s\n", sec->name);
+
+  /* We detached the output section statements for all GNU_ABIND sections
+     earlier.  Find the statement for this output section, and reattach it.  */
+  os_stat = lang_output_section_find (sec->output_section->name);
+  os_stat->region = get_region_for_abind (sec, FALSE);
+
+  /* Find the position in the statement list to insert this GNU_ABIND
+     section.  */
+  for (curr = root->head; curr != NULL; curr = curr->header.next)
+    {
+      switch (curr->header.type)
+	{
+	case lang_output_section_statement_enum:
+	  if (curr->output_section_statement.bfd_section == NULL
+	      || bfd_is_abs_section (curr->output_section_statement.bfd_section))
+	    {
+	      /* Always keep a record of the last output section statement, so a
+		 GNU_ABIND section can be attached after it.  */
+	      prev_os_stat = prev = curr;
+	      continue;
+	    }
+
+	  os = curr->output_section_statement.bfd_section;
+	  if (dump)
+	    printf ("  %s vma 0x%lx size 0x%lx\n", os->name,
+		    os->vma, os->size);
+
+	  if (os->vma >= addr || (os->vma + os->size > addr))
+	    {
+	      /* Between the end of the prev_os_stat and the end of this current
+		 output section is the desired VMA for the GNU_ABIND section.
+		 Insert the GNU_ABIND section after prev_os_stat.
+		 If there is no prev_os_stat insert at the first suitable place in the overall
+		 statement list, using insert_os_after to find that place.  */
+	      if (prev_os_stat == NULL)
+		insert_after = *(insert_os_after ((lang_output_section_statement_type *)root->head));
+	      else
+		insert_after = prev_os_stat;
+	    }
+	  else if (curr->header.next == NULL || is_last_real_os (curr))
+	    {
+	      /* Insert at the end of the statement list/after the last output
+		 section.  */
+	      insert_after = curr;
+	    }
+
+	  if (insert_after != NULL)
+	    {
+	      lang_statement_union_type *new_stmt = insert_os_stat (os_stat);
+	      new_stmt->output_section_statement.addr_tree = exp_intop (addr);
+
+	      if (dump)
+		{
+		  if (insert_after->header.type == lang_output_section_statement_enum
+		      && insert_after->output_section_statement.bfd_section)
+		    printf ("  INSERT %s HERE, after %s\n", sec->name,
+			    insert_after->output_section_statement.bfd_section->name);
+		  else
+		    printf ("  INSERT %s HERE, after unknown\n", sec->name);
+		}
+
+	      new_stmt->header.next = insert_after->header.next;
+	      insert_after->header.next = new_stmt;
+
+	      if (insert_after->header.next == NULL)
+		root->tail = &new_stmt->header.next;
+
+	      return &new_stmt->output_section_statement;
+	    }
+	  prev_os_stat = curr;
+	  break;
+	default:
+	  if (dump)
+	    printf ("  statement type %d\n", curr->header.type);
+	  break;
+	}
+      prev = curr;
+    }
+  return NULL;
+}
+
+/* Place SHF_GNU_ABIND sections at their requested VMAs, if possible.  */
+static bfd_boolean
+place_bound_sections (void)
+{
+  bfd *ibfd;
+  asection *sec;
+  unsigned int num_abind;
+  lang_output_section_statement_type *os_stat;
+  unsigned int i;
+  asection **abind_secs;
+
+  num_abind = sort_abind_secs (&abind_secs);
+
+  if (num_abind == 0)
+    return TRUE;
+
+  /* Detach the GNU_ABIND sections from the global statement list.  This ensures that
+     regular sections are in accurate positions each time we go to look at
+     placing the GNU_ABIND section.
+     Create .abind_*_init_array entries if required.  */
+  for (i = 0; i < num_abind; i++)
+    {
+      static asection *init_sec = NULL;
+      struct elf_link_hash_entry *h;
+
+      sec = abind_secs[i]->output_section;
+
+      /* .bss sections and those with LMA != VMA will require runtime
+	 initialization using .gnu.abind_init_array.  */
+      if ((sec->lma != sec->vma
+	   || (sec->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
+	{
+	  if (init_sec == NULL)
+	    {
+	      asection *s;
+	      /* Find the input section version of .gnu.abind_init_array.
+		 Changes to the size of the output section don't persist.  */
+	      for (ibfd = link_info.input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+		for (s = ibfd->sections; s != NULL; s = s->next)
+		  if (strcmp (s->name, ".gnu.abind_init_array") == 0)
+		    init_sec = s;
+
+	      /* .gnu.abind_init_array should have been created in
+		 elf_link_add_object_symbols.  */
+	      if (init_sec == NULL)
+		{
+		  einfo (_("%X%P: .gnu.abind_init_array has not been created\n"));
+		  return FALSE;
+		}
+	    }
+
+	  /* If the section will require a .gnu.abind_init_array entry, then additional
+	     support from the run time library is required.
+	     Verify that support is available by checking the
+	     __run_gnu_abind_init_array has been resolved.  */
+	  h = elf_link_hash_lookup (elf_hash_table (&link_info),
+				    "__run_gnu_abind_init_array",
+				    FALSE, FALSE, FALSE);
+
+	  if (h == NULL
+	      || (h != NULL && (h->root.type != bfd_link_hash_defined)))
+	    {
+	      /* If we could restore the .gnu.abind section to its original name
+		 and place it in its original output section, maybe this
+		 error wouldn't have to be fatal.  However, if the program requires
+		 the section to be at a specific address, then it will not be
+		 correct if we can't place the ABIND section.  */
+	      einfo (_("%X%P: __run_gnu_abind_init_array symbol has not been resolved\n"));
+
+	      /* Report the remaining abind sections that can't be placed, then
+		 quit.  */
+	      for (; i < num_abind; i++)
+		{
+		  if (!(sec->lma != sec->vma
+			|| (sec->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
+		    continue;
+		  elf_section_flags (abind_secs[i]) &= ~SHF_GNU_ABIND;
+		  elf_section_flags (abind_secs[i]->output_section) &= ~SHF_GNU_ABIND;
+		  einfo (_("%X%P: SHF_GNU_ABIND section %s requires "
+			   "initialization from __run_gnu_abind_init_array\n"), abind_secs[i]->name);
+		}
+	      return FALSE;
+	    }
+	  bfd_set_section_size (init_sec, init_sec->size
+				+ elf_section_data (init_sec)->this_hdr.sh_entsize);
+	}
+
+      if (!detach_output_sec (abind_secs[i]->output_section, stat_ptr, FALSE))
+	return FALSE;
+    }
+
+
+  for (i = 0; i < num_abind; i++)
+    {
+      lang_relax_sections (TRUE);
+      sec = abind_secs[i];
+      os_stat = move_abind_os (sec, stat_ptr);
+
+      if (os_stat == NULL)
+	{
+	  einfo (_("%P: error: unable to place GNU_ABIND section %s\n"), sec->name);
+	  continue;
+	}
+
+      /* Refresh this bound section, which should only
+	 ever contain a SHF_GNU_ABIND input section.  */
+      lang_list_init (&os_stat->children);
+      insert_wild (&os_stat->children);
+      /* We need to clear the output section before calling
+	 lang_add_section.  */
+      sec->output_section = NULL;
+      lang_add_section (&os_stat->children.head->wild_statement.children,
+			sec, NULL, os_stat);
+    }
+
+  if (!insert_abind_init_array_symbols ())
+    {
+      einfo (_("%P: couldn't find .gnu.abind_init_array section to define "
+	       "__gnu_abind_init_array_{start,end} symbols"));
+      return FALSE;
+    }
+  return TRUE;
+}
+
+static void ATTRIBUTE_UNUSED
+dump_sections (lang_statement_list_type *root, lang_memory_region_type *r, int indent)
+{
+  lang_statement_union_type * curr;
+  lang_output_section_statement_type * os_stat;
+  asection *s;
+  int i;
+
+  for (curr = root->head; curr != NULL; curr = curr->header.next)
+    {
+      switch (curr->header.type)
+	{
+	case lang_input_section_enum:
+	  s = curr->input_section.section;
+
+	  for (i = 0; i < indent; i++)
+	    printf ("  ");
+	  if (s)
+	    printf ("%s\n", s->name);
+	  else
+	    printf ("unknown input section\n");
+
+	  break;
+
+	case lang_output_section_statement_enum:
+	  os_stat = &curr->output_section_statement;
+	  for (i = 0; i < indent; i++)
+	    printf ("  ");
+	  if (r != os_stat->region)
+	    {
+	      r = os_stat->region;
+	      if (r)
+		printf ("\nNEW REGION %s\n---\n", r->name_list.name);
+	      else
+		printf ("\nNEW UNKNOWN REGION\n---\n");
+	    }
+	  s = curr->output_section_statement.bfd_section;
+	  for (i = 0; i < indent; i++)
+	    printf ("  ");
+	  if (s)
+	    {
+	      printf ("%s LMA: 0x%lx VMA: 0x%lx", s->name, s->lma, s->vma);
+	      if (os_stat->lma_region)
+		printf (" LMA region = %s", os_stat->lma_region->name_list.name);
+	      if (os_stat->region)
+		printf (" VMA region = %s", os_stat->region->name_list.name);
+	      printf ("\n");
+	    }
+	  else
+	    printf ("unknown output section, curr region vma 0x%lx\n", os_stat->region->current);
+
+	  dump_sections (&curr->output_section_statement.children, curr->output_section_statement.region, indent + 1);
+	  break;
+
+	case lang_wild_statement_enum:
+	  for (i = 0; i < indent; i++)
+	    printf ("  ");
+	  printf ("wild statement\n");
+	  dump_sections (&curr->wild_statement.children, r, indent + 1);
+	  break;
+
+	case lang_group_statement_enum:
+	  dump_sections (&curr->group_statement.children, r, indent + 1);
+	  break;
+
+	case lang_assignment_statement_enum:
+	  if (1)
+	    {
+	      lang_assignment_statement_type *ass;
+
+	      ass = &curr->assignment_statement;
+	      for (i = 0; i < indent; i++)
+		printf ("  ");
+	      printf ("assignment statement, dst = %s\n", ass->exp->assign.dst);
+	    }
+	  break;
+
+	default:
+	  break;
+	}
+    }
+}
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 4249b3a045..b8d260f780 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -1345,6 +1345,12 @@ static lang_memory_region_type *lang_memory_region_list;
 static lang_memory_region_type **lang_memory_region_list_tail
   = &lang_memory_region_list;
 
+lang_memory_region_type *
+get_memory_region_list ()
+{
+  return lang_memory_region_list;
+}
+
 lang_memory_region_type *
 lang_memory_region_lookup (const char *const name, bfd_boolean create)
 {
@@ -1798,7 +1804,7 @@ output_prev_sec_find (lang_output_section_statement_type *os)
    insert non-alloc note sections among assignments setting end of
    image symbols.  */
 
-static lang_statement_union_type **
+lang_statement_union_type **
 insert_os_after (lang_output_section_statement_type *after)
 {
   lang_statement_union_type **where;
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 196debfa37..631e50d027 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -520,6 +520,8 @@ extern struct asneeded_minfo **asneeded_list_tail;
 
 extern void (*output_bfd_hash_table_free_fn) (struct bfd_link_hash_table *);
 
+extern lang_memory_region_type * get_memory_region_list (void);
+
 extern void lang_init
   (void);
 extern void lang_finish
@@ -595,6 +597,9 @@ extern void ldlang_add_file
 extern lang_output_section_statement_type *lang_output_section_find_by_flags
   (const asection *, flagword, lang_output_section_statement_type **,
    lang_match_sec_type_func);
+
+extern lang_statement_union_type ** insert_os_after
+(lang_output_section_statement_type *after);
 extern lang_output_section_statement_type *lang_insert_orphan
   (asection *, const char *, int, lang_output_section_statement_type *,
    struct orphan_save *, etree_type *, lang_statement_list_type *);
diff --git a/ld/testsuite/ld-arm/abind-1.d b/ld/testsuite/ld-arm/abind-1.d
new file mode 100644
index 0000000000..1b2ad44d56
--- /dev/null
+++ b/ld/testsuite/ld-arm/abind-1.d
@@ -0,0 +1,49 @@
+#name: SHF_GNU_ABIND 1 (using default linker script)
+#source: abind-1.s
+#ld: -e _start
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+00008000 t addr_0x8000_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+0000b002 r addr_0xb002
+#...
+0000b100 t addr_0xb100
+#...
+0000b200 r addr_0xb200_size_0x1000
+#...
+0000c402 d addr_0xc402
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0000e000 b addr_0xe000_size_0x1000
+#...
+0000f000 b addr_0xf000
+#pass
diff --git a/ld/testsuite/ld-arm/abind-1.s b/ld/testsuite/ld-arm/abind-1.s
new file mode 100644
index 0000000000..68183a3bbb
--- /dev/null
+++ b/ld/testsuite/ld-arm/abind-1.s
@@ -0,0 +1,183 @@
+/* abind-2*.ld linker scripts don't have rules for uniquely named sections.
+   This means they will be orphaned which will better test the intermixing of
+   .abind and regular sections, instead of just having one large indivisible
+   block for each output section.  */
+
+/* Arbitrary sized .data sections. */
+	.section	.data.size_0x1,"aw"
+	.type	data_size_0x1, %object
+data_size_0x1:
+	.zero	0x1
+
+	.section	.data.size_0x10,"aw"
+	.type	data_size_0x10, %object
+data_size_0x10:
+	.zero	0x10
+
+	.section	.data.size_0x80,"aw"
+	.type	data_size_0x80, %object
+data_size_0x80:
+	.zero	0x80
+
+	.section	.data.size_0x100,"aw"
+	.type	data_size_0x100, %object
+data_size_0x100:
+	.zero	0x100
+
+	.section	.data.size_0x800,"aw"
+	.type	data_size_0x800, %object
+data_size_0x800:
+	.zero	0x800
+
+	.section	.data.size_0x1000,"aw"
+	.type	data_size_0x1000, %object
+data_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .bss sections.  */
+
+	.section	.bss.size_0x1,"aw",%nobits
+	.type	bss_size_0x1, %object
+bss_size_0x1:
+	.zero	0x1
+
+	.section	.bss.size_0x10,"aw",%nobits
+	.type	bss_size_0x10, %object
+bss_size_0x10:
+	.zero	0x10
+
+	.section	.bss.size_0x80,"aw",%nobits
+	.type	bss_size_0x80, %object
+bss_size_0x80:
+	.zero	0x80
+
+	.section	.bss.size_0x100,"aw",%nobits
+	.type	bss_size_0x100, %object
+bss_size_0x100:
+	.zero	0x100
+
+	.section	.bss.size_0x800,"aw",%nobits
+	.type	bss_size_0x800, %object
+bss_size_0x800:
+	.zero	0x800
+
+	.section	.bss.size_0x1000,"aw",%nobits
+	.type	bss_size_0x1000, %object
+bss_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .text sections.  */
+
+	.section	.text.size_0x1,"ax",%progbits
+	.type	text_size_0x1, %function
+text_size_0x1:
+	.zero	0x1
+
+	.section	.text.size_0x10,"ax",%progbits
+	.type	text_size_0x10, %function
+text_size_0x10:
+	.zero	0x10
+
+	.section	.text.size_0x80,"ax",%progbits
+	.type	text_size_0x80, %function
+text_size_0x80:
+	.zero	0x80
+
+	.section	.text.size_0x100,"ax",%progbits
+	.type	text_size_0x100, %function
+text_size_0x100:
+	.zero	0x100
+
+	.section	.text.size_0x800,"ax",%progbits
+	.type	text_size_0x800, %function
+text_size_0x800:
+	.zero	0x800
+
+	.section	.text.size_0x1000,"ax",%progbits
+	.type	text_size_0x1000, %function
+text_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .rodata sections.  */
+
+	.section	.rodata.size_0x1,"a"
+	.type	rodata_size_0x1, %object
+rodata_size_0x1:
+	.zero	0x1
+
+	.section	.rodata.size_0x10,"a"
+	.type	rodata_size_0x10, %object
+rodata_size_0x10:
+	.zero	0x10
+
+	.section	.rodata.size_0x80,"a"
+	.type	rodata_size_0x80, %object
+rodata_size_0x80:
+	.zero	0x80
+
+	.section	.rodata.size_0x100,"a"
+	.type	rodata_size_0x100, %object
+rodata_size_0x100:
+	.zero	0x100
+
+	.section	.rodata.size_0x800,"a"
+	.type	rodata_size_0x800, %object
+rodata_size_0x800:
+	.zero	0x800
+
+	.section	.rodata.size_0x1000,"a"
+	.type	rodata_size_0x1000, %object
+rodata_size_0x1000:
+	.zero	0x1000
+
+/* Start .gnu.abind sections.  */
+
+/* Fix a large section to the start of the region used for the LMA region of
+   the .data sections.  This will expose any placement issues causing
+   overlapping LMAs.  */
+	.section	.text.addr_0x8000,"axA",%progbits,0x8000
+	.type	addr_0x8000, %function
+addr_0x8000_size_0x1000:
+	.zero 0x1000
+
+	.section	.rodata.addr_0xb002,"aA",0xb002
+	.type	addr_0xb002, %object
+addr_0xb002:
+	.byte	2
+
+	.section	.text.addr_0xb100,"axA",%progbits,0xb100
+	.type	addr_0xb100, %function
+addr_0xb100:
+	.zero 2
+
+	.section	.rodata.addr_0xb200,"aA",0xb200
+	.type	addr_0xb200, %object
+addr_0xb200_size_0x1000:
+	.zero 0x1000
+
+	.section	.data.addr_0xc402,"awA",0xc402
+	.type	addr_0xc402, %object
+addr_0xc402:
+	.zero 2
+
+	.section	.bss.addr_0xe000,"awA",%nobits,0xe000
+	.type	addr_0xe000, %object
+addr_0xe000_size_0x1000:
+	.zero 0x1000
+
+	.section	.bss.addr_0xf000,"awA",%nobits,0x0f000
+	.type	addr_0xf000, %object
+addr_0xf000:
+	.zero	1
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
+
+	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
+	.global	__run_gnu_abind_init_array
+	.type	__run_gnu_abind_init_array, %function
+__run_gnu_abind_init_array:
+	.word 0
diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp
index 0b47d636df..11b06b28a6 100644
--- a/ld/testsuite/ld-arm/arm-elf.exp
+++ b/ld/testsuite/ld-arm/arm-elf.exp
@@ -1269,6 +1269,8 @@ run_dump_test "non-contiguous-arm4"
 run_dump_test "non-contiguous-arm5"
 run_dump_test "non-contiguous-arm6"
 
+run_dump_test "abind-1"
+
 if { ![istarget "arm*-*-nacl*"] } {
     run_dump_test "thumb-plt"
     run_dump_test "thumb-plt-got"
diff --git a/ld/testsuite/ld-elf/abind-1.s b/ld/testsuite/ld-elf/abind-1.s
new file mode 100644
index 0000000000..aa3f1fcf82
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1.s
@@ -0,0 +1,57 @@
+/* This should get placed before addr_0x8200, at 0x8000.  */
+	.section	.data.size_0x100,"aw"
+	.type	size_0x100, %object
+size_0x100:
+	.zero	0x100
+
+/* This should get placed after addr_0x8200.  */
+	.section	.bss.size_0x150,"aw",%nobits
+	.type	size_0x150, %object
+size_0x150:
+	.zero	0x150
+
+	.section	.data.addr_0x8200,"awA",0x8200
+	.type	addr_0x8200, %object
+addr_0x8200:
+	.short	1
+
+	.section	.bss.addr_0x9000,"awA",%nobits,0x9000
+	.type	addr_0x9000, %object
+addr_0x9000:
+	.zero	1
+
+/* This should be the last section in the region.  */
+	.section	.bss.addr_0xa000,"awA",%nobits,0xa000
+	.type	addr_0xa000, %object
+addr_0xa000:
+	.zero	1
+
+/* Fix a large section to the start of the region used for the LMA region of
+   the .data sections.  This will expose any placement issues causing
+   overlapping LMAs.  */
+	.section	.text.addr_0x0,"axA",%progbits,0x0
+	.type	addr_0x0, %function
+addr_0x0:
+	.zero 0x1000
+
+	.section	.text.size_0x500,"ax",%progbits
+	.type	size_0x500, %function
+size_0x500:
+	.zero 0x1000
+
+	.section	.rodata.addr_0x1002,"aA",0x1002
+	.type	addr_0x1002, %object
+addr_0x1002:
+	.byte	2
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
+
+	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
+	.global	__run_gnu_abind_init_array
+	.type	__run_gnu_abind_init_array, %function
+__run_gnu_abind_init_array:
+	.word 0
diff --git a/ld/testsuite/ld-elf/abind-1a.d b/ld/testsuite/ld-elf/abind-1a.d
new file mode 100644
index 0000000000..1b758fef59
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1a.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1a
+#source: abind-1.s
+#ld: -e _start -T abind-1a.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . size_0x150
+#...
+0+8200 . addr_0x8200
+#...
+[0-9a-f]+ . size_0x100
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1a.ld b/ld/testsuite/ld-elf/abind-1a.ld
new file mode 100644
index 0000000000..dd3a18ef70
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1a.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > ROM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM AT> ROM
+}
diff --git a/ld/testsuite/ld-elf/abind-1b.d b/ld/testsuite/ld-elf/abind-1b.d
new file mode 100644
index 0000000000..41c926529a
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1b.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1b
+#source: abind-1.s
+#ld: -e _start -T abind-1b.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . size_0x100
+#...
+0+8200 . addr_0x8200
+#...
+[0-9a-f]+ . size_0x150
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1b.ld b/ld/testsuite/ld-elf/abind-1b.ld
new file mode 100644
index 0000000000..99f9163b7f
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1b.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
+}
+
+SECTIONS
+{
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM AT> ROM
+
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > ROM
+}
diff --git a/ld/testsuite/ld-elf/abind-1c.d b/ld/testsuite/ld-elf/abind-1c.d
new file mode 100644
index 0000000000..740e6b112d
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1c.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1c
+#source: abind-1.s
+#ld: -e _start -T abind-1c.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . size_0x150
+#...
+0+8200 . addr_0x8200
+#...
+[0-9a-f]+ . size_0x100
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1c.ld b/ld/testsuite/ld-elf/abind-1c.ld
new file mode 100644
index 0000000000..1587ef028e
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1c.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > ROM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM AT> ROM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > ROM
+}
diff --git a/ld/testsuite/ld-elf/abind-1d.d b/ld/testsuite/ld-elf/abind-1d.d
new file mode 100644
index 0000000000..9c2e64dc4c
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1d.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1d
+#source: abind-1.s
+#ld: -e _start -T abind-1d.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . size_0x150
+#...
+0+8200 . addr_0x8200
+#...
+[0-9a-f]+ . size_0x100
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1d.ld b/ld/testsuite/ld-elf/abind-1d.ld
new file mode 100644
index 0000000000..852e03e645
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1d.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM AT> ROM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > ROM
+}
diff --git a/ld/testsuite/ld-elf/abind-1e.d b/ld/testsuite/ld-elf/abind-1e.d
new file mode 100644
index 0000000000..ac1a78654f
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1e.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1e (No LMA region)
+#source: abind-1.s
+#ld: -e _start -T abind-1e.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x150
+#...
+[0-9a-f]+ . size_0x100
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+0+8200 . addr_0x8200
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1e.ld b/ld/testsuite/ld-elf/abind-1e.ld
new file mode 100644
index 0000000000..58b3dc075a
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1e.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xefff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-1f.d b/ld/testsuite/ld-elf/abind-1f.d
new file mode 100644
index 0000000000..2981a4e6d1
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1f.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1f (Placement in other region)
+#source: abind-1.s
+#ld: -e _start -T abind-1f.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x150
+#...
+[0-9a-f]+ . size_0x100
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+0+8200 . addr_0x8200
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1f.ld b/ld/testsuite/ld-elf/abind-1f.ld
new file mode 100644
index 0000000000..7a655e0262
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1f.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0x9fff
+  VECT     		: ORIGIN = 0xa000,	LENGTH = 0x4
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-1g.d b/ld/testsuite/ld-elf/abind-1g.d
new file mode 100644
index 0000000000..90807c4951
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1g.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1g (Placement outside region)
+#source: abind-1.s
+#ld: -e _start -T abind-1g.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x150
+#...
+[0-9a-f]+ . size_0x100
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+0+8200 . addr_0x8200
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1g.ld b/ld/testsuite/ld-elf/abind-1g.ld
new file mode 100644
index 0000000000..8749d74626
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1g.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0x9fff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-1h.d b/ld/testsuite/ld-elf/abind-1h.d
new file mode 100644
index 0000000000..a17f5d47a0
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1h.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1h (No flags set on region)
+#source: abind-1.s
+#ld: -e _start -T abind-1h.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x150
+#...
+[0-9a-f]+ . size_0x100
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+0+8200 . addr_0x8200
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1h.ld b/ld/testsuite/ld-elf/abind-1h.ld
new file mode 100644
index 0000000000..8092bb40d6
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1h.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM 		: ORIGIN = 0x0000,	LENGTH = 0xafff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  } > RAM
+
+  .data :
+  {
+    *(.data .data.*)
+  } > RAM
+
+  .text :
+  {
+    *(.text .text.*)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-1i.d b/ld/testsuite/ld-elf/abind-1i.d
new file mode 100644
index 0000000000..17696c3760
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1i.d
@@ -0,0 +1,27 @@
+#name: SHF_GNU_ABIND 1i (No memory regions)
+#source: abind-1.s
+#ld: -e _start -T abind-1i.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 . addr_0x0
+#...
+0+1002 . addr_0x1002
+#...
+[0-9a-f]+ . size_0x150
+#...
+[0-9a-f]+ . size_0x100
+#...
+[0-9a-f]+ . size_0x500
+#...
+[0-9a-f]+ . _start
+#...
+[0-9a-f]+ . __run_gnu_abind_init_array
+#...
+0+8200 . addr_0x8200
+#...
+0+9000 . addr_0x9000
+#...
+0+a000 . addr_0xa000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-1i.ld b/ld/testsuite/ld-elf/abind-1i.ld
new file mode 100644
index 0000000000..9b763c7387
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-1i.ld
@@ -0,0 +1,22 @@
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss .bss.*)
+  }
+
+  .data :
+  {
+    *(.data .data.*)
+  }
+
+  .text :
+  {
+    *(.text .text.*)
+  }
+
+  .rodata :
+  {
+    *(.rodata .rodata.*)
+  }
+}
diff --git a/ld/testsuite/ld-elf/abind-2.s b/ld/testsuite/ld-elf/abind-2.s
new file mode 100644
index 0000000000..75777e4817
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2.s
@@ -0,0 +1,183 @@
+/* abind-2*.ld linker scripts don't have rules for uniquely named sections.
+   This means they will be orphaned which will better test the intermixing of
+   .abind and regular sections, instead of just having one large indivisible
+   block for each output section.  */
+
+/* Arbitrary sized .data sections. */
+	.section	.data.size_0x1,"aw"
+	.type	data_size_0x1, %object
+data_size_0x1:
+	.zero	0x1
+
+	.section	.data.size_0x10,"aw"
+	.type	data_size_0x10, %object
+data_size_0x10:
+	.zero	0x10
+
+	.section	.data.size_0x80,"aw"
+	.type	data_size_0x80, %object
+data_size_0x80:
+	.zero	0x80
+
+	.section	.data.size_0x100,"aw"
+	.type	data_size_0x100, %object
+data_size_0x100:
+	.zero	0x100
+
+	.section	.data.size_0x800,"aw"
+	.type	data_size_0x800, %object
+data_size_0x800:
+	.zero	0x800
+
+	.section	.data.size_0x1000,"aw"
+	.type	data_size_0x1000, %object
+data_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .bss sections.  */
+
+	.section	.bss.size_0x1,"aw",%nobits
+	.type	bss_size_0x1, %object
+bss_size_0x1:
+	.zero	0x1
+
+	.section	.bss.size_0x10,"aw",%nobits
+	.type	bss_size_0x10, %object
+bss_size_0x10:
+	.zero	0x10
+
+	.section	.bss.size_0x80,"aw",%nobits
+	.type	bss_size_0x80, %object
+bss_size_0x80:
+	.zero	0x80
+
+	.section	.bss.size_0x100,"aw",%nobits
+	.type	bss_size_0x100, %object
+bss_size_0x100:
+	.zero	0x100
+
+	.section	.bss.size_0x800,"aw",%nobits
+	.type	bss_size_0x800, %object
+bss_size_0x800:
+	.zero	0x800
+
+	.section	.bss.size_0x1000,"aw",%nobits
+	.type	bss_size_0x1000, %object
+bss_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .text sections.  */
+
+	.section	.text.size_0x1,"ax",%progbits
+	.type	text_size_0x1, %function
+text_size_0x1:
+	.zero	0x1
+
+	.section	.text.size_0x10,"ax",%progbits
+	.type	text_size_0x10, %function
+text_size_0x10:
+	.zero	0x10
+
+	.section	.text.size_0x80,"ax",%progbits
+	.type	text_size_0x80, %function
+text_size_0x80:
+	.zero	0x80
+
+	.section	.text.size_0x100,"ax",%progbits
+	.type	text_size_0x100, %function
+text_size_0x100:
+	.zero	0x100
+
+	.section	.text.size_0x800,"ax",%progbits
+	.type	text_size_0x800, %function
+text_size_0x800:
+	.zero	0x800
+
+	.section	.text.size_0x1000,"ax",%progbits
+	.type	text_size_0x1000, %function
+text_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .rodata sections.  */
+
+	.section	.rodata.size_0x1,"a"
+	.type	rodata_size_0x1, %object
+rodata_size_0x1:
+	.zero	0x1
+
+	.section	.rodata.size_0x10,"a"
+	.type	rodata_size_0x10, %object
+rodata_size_0x10:
+	.zero	0x10
+
+	.section	.rodata.size_0x80,"a"
+	.type	rodata_size_0x80, %object
+rodata_size_0x80:
+	.zero	0x80
+
+	.section	.rodata.size_0x100,"a"
+	.type	rodata_size_0x100, %object
+rodata_size_0x100:
+	.zero	0x100
+
+	.section	.rodata.size_0x800,"a"
+	.type	rodata_size_0x800, %object
+rodata_size_0x800:
+	.zero	0x800
+
+	.section	.rodata.size_0x1000,"a"
+	.type	rodata_size_0x1000, %object
+rodata_size_0x1000:
+	.zero	0x1000
+
+/* Start .gnu.abind sections.  */
+
+/* Fix a large section to the start of the region used for the LMA region of
+   the .data sections.  This will expose any placement issues causing
+   overlapping LMAs.  */
+	.section	.text.addr_0x0,"axA",%progbits,0x0
+	.type	addr_0x0, %function
+addr_0x0_size_0x1000:
+	.zero 0x1000
+
+	.section	.rodata.addr_0x1002,"aA",0x1002
+	.type	addr_0x1002, %object
+addr_0x1002:
+	.byte	2
+
+	.section	.text.addr_0x2000,"axA",%progbits,0x2000
+	.type	addr_0x2000, %function
+addr_0x2000:
+	.zero 2
+
+	.section	.rodata.addr_0x3002,"aA",0x3002
+	.type	addr_0x3002, %object
+addr_0x3002_size_0x1000:
+	.zero 0x1000
+
+	.section	.data.addr_0x8000,"awA",0x8000
+	.type	addr_0x8000, %object
+addr_0x8000:
+	.zero 2
+
+	.section	.bss.addr_0x9000,"awA",%nobits,0x9000
+	.type	addr_0x9000, %object
+addr_0x9000_size_0x1000:
+	.zero 0x1000
+
+	.section	.bss.addr_0xd000,"awA",%nobits,0xd000
+	.type	addr_0xd000, %object
+addr_0xd000:
+	.zero	1
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
+
+	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
+	.global	__run_gnu_abind_init_array
+	.type	__run_gnu_abind_init_array, %function
+__run_gnu_abind_init_array:
+	.word 0
diff --git a/ld/testsuite/ld-elf/abind-2a.d b/ld/testsuite/ld-elf/abind-2a.d
new file mode 100644
index 0000000000..ec247ac951
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2a.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2a
+#source: abind-2.s
+#ld: -e _start -T abind-2a.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+8000 d addr_0x8000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2a.ld b/ld/testsuite/ld-elf/abind-2a.ld
new file mode 100644
index 0000000000..517366f395
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2a.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
+}
+
+SECTIONS
+{
+  .text :
+  {
+    *(.text)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > ROM
+
+  .data :
+  {
+    *(.data)
+  } > RAM AT> ROM
+
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-2b.d b/ld/testsuite/ld-elf/abind-2b.d
new file mode 100644
index 0000000000..66057ab3bd
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2b.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2b
+#source: abind-2.s
+#ld: -e _start -T abind-2b.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+8000 d addr_0x8000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2b.ld b/ld/testsuite/ld-elf/abind-2b.ld
new file mode 100644
index 0000000000..e180fabedb
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2b.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
+  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
+}
+
+SECTIONS
+{
+  .text :
+  {
+    *(.text)
+  } > ROM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > ROM
+
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+
+  .data :
+  {
+    *(.data)
+  } > RAM AT> ROM
+}
diff --git a/ld/testsuite/ld-elf/abind-2c.d b/ld/testsuite/ld-elf/abind-2c.d
new file mode 100644
index 0000000000..74f62a36f1
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2c.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2c
+#source: abind-2.s
+#ld: -e _start -T abind-2c.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+0+8000 d addr_0x8000
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2c.ld b/ld/testsuite/ld-elf/abind-2c.ld
new file mode 100644
index 0000000000..3c553f4b31
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2c.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xefff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+
+  .data :
+  {
+    *(.data)
+  } > RAM
+
+  .text :
+  {
+    *(.text)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-2d.d b/ld/testsuite/ld-elf/abind-2d.d
new file mode 100644
index 0000000000..1fb480843f
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2d.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2d
+#source: abind-2.s
+#ld: -e _start -T abind-2d.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+0+8000 d addr_0x8000
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2d.ld b/ld/testsuite/ld-elf/abind-2d.ld
new file mode 100644
index 0000000000..00b375bfbe
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2d.ld
@@ -0,0 +1,28 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xcfff
+  VECT     		: ORIGIN = 0xd000,	LENGTH = 0x4
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+
+  .data :
+  {
+    *(.data)
+  } > RAM
+
+  .text :
+  {
+    *(.text)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-2e.d b/ld/testsuite/ld-elf/abind-2e.d
new file mode 100644
index 0000000000..ff15f34de8
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2e.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2e
+#source: abind-2.s
+#ld: -e _start -T abind-2e.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+0+8000 d addr_0x8000
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2e.ld b/ld/testsuite/ld-elf/abind-2e.ld
new file mode 100644
index 0000000000..b1bb54b3a4
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2e.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xcfff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+
+  .data :
+  {
+    *(.data)
+  } > RAM
+
+  .text :
+  {
+    *(.text)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-2f.d b/ld/testsuite/ld-elf/abind-2f.d
new file mode 100644
index 0000000000..8a59b5da79
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2f.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2f
+#source: abind-2.s
+#ld: -e _start -T abind-2f.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+0+8000 d addr_0x8000
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2f.ld b/ld/testsuite/ld-elf/abind-2f.ld
new file mode 100644
index 0000000000..4867878538
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2f.ld
@@ -0,0 +1,27 @@
+MEMORY
+{
+  RAM 		: ORIGIN = 0x0000,	LENGTH = 0xdfff
+}
+
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss)
+  } > RAM
+
+  .data :
+  {
+    *(.data)
+  } > RAM
+
+  .text :
+  {
+    *(.text)
+  } > RAM
+
+  .rodata :
+  {
+    *(.rodata)
+  } > RAM
+}
diff --git a/ld/testsuite/ld-elf/abind-2g.d b/ld/testsuite/ld-elf/abind-2g.d
new file mode 100644
index 0000000000..7084f5fcee
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2g.d
@@ -0,0 +1,73 @@
+#name: SHF_GNU_ABIND 2g
+#source: abind-2.s
+#ld: -e _start -T abind-2g.ld
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+0000 t addr_0x0_size_0x1000
+#...
+[0-9a-f]+ . bss_size_0x1
+#...
+0+1002 r addr_0x1002
+#...
+[0-9a-f]+ . bss_size_0x10
+#...
+[0-9a-f]+ . bss_size_0x80
+#...
+[0-9a-f]+ . bss_size_0x100
+#...
+[0-9a-f]+ . bss_size_0x800
+#...
+0+2000 t addr_0x2000
+#...
+[0-9a-f]+ . bss_size_0x1000
+#...
+0+3002 r addr_0x3002_size_0x1000
+#...
+[0-9a-f]+ . data_size_0x1
+#...
+[0-9a-f]+ . data_size_0x10
+#...
+[0-9a-f]+ . data_size_0x80
+#...
+[0-9a-f]+ . data_size_0x100
+#...
+[0-9a-f]+ . data_size_0x800
+#...
+[0-9a-f]+ . data_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+#...
+[0-9a-f]+ . text_size_0x10
+#...
+[0-9a-f]+ . text_size_0x80
+#...
+[0-9a-f]+ . text_size_0x100
+#...
+[0-9a-f]+ . text_size_0x800
+#...
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+#...
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+[0-9a-f]+ . rodata_size_0x1
+#...
+[0-9a-f]+ . rodata_size_0x10
+#...
+[0-9a-f]+ . rodata_size_0x80
+#...
+[0-9a-f]+ . rodata_size_0x100
+#...
+[0-9a-f]+ . rodata_size_0x800
+#...
+0+8000 d addr_0x8000
+#...
+0+9000 b addr_0x9000_size_0x1000
+#...
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+d000 b addr_0xd000
+#pass
diff --git a/ld/testsuite/ld-elf/abind-2g.ld b/ld/testsuite/ld-elf/abind-2g.ld
new file mode 100644
index 0000000000..2a9be2b5c8
--- /dev/null
+++ b/ld/testsuite/ld-elf/abind-2g.ld
@@ -0,0 +1,22 @@
+SECTIONS
+{
+  .bss :
+  {
+    *(.bss)
+  }
+
+  .data :
+  {
+    *(.data)
+  }
+
+  .text :
+  {
+    *(.text)
+  }
+
+  .rodata :
+  {
+    *(.rodata)
+  }
+}
diff --git a/ld/testsuite/ld-msp430-elf/location-1.d b/ld/testsuite/ld-msp430-elf/location-1.d
new file mode 100644
index 0000000000..9a23431033
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-1.d
@@ -0,0 +1,23 @@
+# Test that text, rodata, data and bss locsyms get placed at the correct
+# addresses.
+# The variable "either" should be placed in between the .data sections.
+# 
+#name: SMK_LOCATION data meta-information 
+#ld: -e _start
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+00000510 . loc510_data
+00000520 . loc520_data
+000005f1 . loc5f1_data
+#...
+[0-9a-f]+ . either
+#...
+00000790 . loc790_data
+00000808 . loc808_data
+00000850 . loc850_bss
+#...
+00009000 . loc9000_text
+0000910a . loc910a_rodata
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/location-1.s b/ld/testsuite/ld-msp430-elf/location-1.s
new file mode 100644
index 0000000000..83d179d281
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-1.s
@@ -0,0 +1,98 @@
+	.global	arr_1
+	.section	.data.arr_1,"aw"
+	.type	arr_1, %object
+arr_1:
+	.zero	80
+
+	.global	arr_2
+	.section	.data.arr_2,"aw"
+	.type	arr_2, %object
+arr_2:
+	.zero	80
+
+	.global	arr_3
+	.section	.data.arr_3,"aw"
+	.type	arr_3, %object
+arr_3:
+	.zero	80
+
+	.global	arr_4
+	.section	.data.arr_4,"aw"
+	.type	arr_4, %object
+arr_4:
+	.zero	80
+
+/*
+	.global	const_arr_1
+	.section	.rodata.const_arr_1,"a"
+	.type	const_arr_1, %object
+const_arr_1:
+	.zero	80
+  */
+
+
+	.global	loc808_data
+	.section	.data.loc808_data,"awA",0x808
+	.type	loc808_data, %object
+loc808_data:
+	.short	2056
+
+	.global	loc510_data
+	.section	.data.loc510_data,"awA",0x510
+	.type	loc510_data, %object
+loc510_data:
+	.byte	81
+
+	.global	loc520_data
+	.section	.data.loc520_data,"awA",0x520
+	.type	loc520_data, %object
+loc520_data:
+	.byte	82
+
+	.global	loc790_data
+	.section	.data.loc790_data,"awA",0x790
+	.type	loc790_data, %object
+loc790_data:
+	.byte	114
+
+	.global	loc5f1_data
+	.section	.data.loc5f1_data,"awA",0x5f1
+	.type	loc5f1_data, %object
+loc5f1_data:
+	.byte	95
+
+	.section	.text.loc9000_text,"axA",%progbits,0x9000
+	.global	loc9000_text
+	.type	loc9000_text, %function
+loc9000_text:
+	.word 0
+
+	.global	loc910a_rodata
+	.section	.rodata.loc910a_rodata,"aA",0x910a
+	.type	loc910a_rodata, %object
+loc910a_rodata:
+	.byte	2
+
+	.global	loc850_bss
+	.section	.bss.loc850_bss,"awA",%nobits,0x850
+	.type	loc850_bss, %object
+loc850_bss:
+	.zero	1
+
+	.global	normal_bss
+	.section	.bss.normal_bss,"aw",%nobits
+	.type	normal_bss, %object
+normal_bss:
+	.zero	1
+
+	.global	either
+	.section	.either.data,"aw"
+	.type	either, %object
+either:
+	.byte	1
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-msp430-elf/location-2.d b/ld/testsuite/ld-msp430-elf/location-2.d
new file mode 100644
index 0000000000..87f3d4555e
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-2.d
@@ -0,0 +1,30 @@
+# Test that relaxation of branch instructions to jumps does not interfere
+# with accurrate placement of sections.
+# Before location placement, the BR instructions are all close enough to their
+# destination such that they can be relaxed to JMP.  However, after placement
+# of the sections, some of the JMP will now be out of range, so check that they
+# get relaxed back to a BR (tested by the fact there are no relocation
+# overflows), and that the functions still get placed at the
+# correct address.
+#name: SMK_LOCATION meta-information placement after relaxation
+#ld: -e _start
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+8000 . fn_8000
+#...
+0+8200 . fn_8200
+#...
+0+8500 . fn_dst
+#...
+0+8600 . fn_8600
+#...
+0+8700 . fn_8700
+#...
+0+8a00 . fn_8a00
+#...
+0+9000 . fn_9000
+#...
+0+910a . loc910a_rodata
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/location-2.s b/ld/testsuite/ld-msp430-elf/location-2.s
new file mode 100644
index 0000000000..0c278a1f78
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-2.s
@@ -0,0 +1,76 @@
+	.global	loc910a_rodata
+	.section	.rodata.loc910a_rodata,"a"
+	.type	loc910a_rodata, @object
+loc910a_rodata:
+	.byte	2
+	.location 0x910a
+
+	.section	.text.fn_8700,"ax",@progbits
+	.global	fn_8700
+	.type	fn_8700, @function
+fn_8700:
+	br #fn_dst
+	br #fn_9000
+	NOP
+	RET
+	.section	.text.fn_8a00,"ax",@progbits
+	.global	fn_8a00
+	.type	fn_8a00, @function
+fn_8a00:
+	br #fn_dst
+	br #fn_9000
+	NOP
+	RET
+	.section	.text.fn_8600,"ax",@progbits
+	.global	fn_8600
+	.type	fn_8600, @function
+fn_8600:
+	br #fn_dst
+	br #fn_9000
+	NOP
+	RET
+	.section	.text.fn_8000,"ax",@progbits
+	.global	fn_8000
+	.type	fn_8000, @function
+fn_8000:
+	br #fn_dst
+	br #fn_9000
+	NOP
+	RET
+	.section	.text.fn_8200,"ax",@progbits
+	.global	fn_8200
+	.type	fn_8200, @function
+fn_8200:
+	br #fn_dst
+	br #fn_9000
+	NOP
+	RET
+  .section	.text.fn_9000,"ax",@progbits
+	.global	fn_9000
+	.type	fn_9000, @function
+fn_9000:
+	br #fn_dst
+	NOP
+	RET
+	.section	.text.fn_dst,"ax",@progbits
+	.global	fn_dst
+	.type	fn_dst, @function
+fn_dst:
+	add r12, r12
+	NOP
+	RET
+	.location  0x8000, .text.fn_8000
+	.location 0x8500, .text.fn_dst
+	.location  0x8200, .text.fn_8200
+	.location  0x9000, .text.fn_9000
+	.location  0x8600, .text.fn_8600
+	.location  0x8700, .text.fn_8700
+	.location  0x8a00, .text.fn_8a00
+	.section	.text._start,"ax",@progbits
+	.global	_start
+	.type	_start, @function
+_start:
+	CALL	#fn_8000
+	MOV.B	#0, R12
+	.refsym	__crt0_call_exit
+	RET
diff --git a/ld/testsuite/ld-msp430-elf/location-3.d b/ld/testsuite/ld-msp430-elf/location-3.d
new file mode 100644
index 0000000000..0a2becf6fc
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-3.d
@@ -0,0 +1,10 @@
+# Test that a text locsym in a random section still gets placed.
+# 
+#name: Orphaned location section
+#ld: -e _start
+#notarget: ![supports_gnu_osabi]
+#nm : -n
+
+#...
+00009100 . loc9100_text
+#pass
diff --git a/ld/testsuite/ld-msp430-elf/location-3.s b/ld/testsuite/ld-msp430-elf/location-3.s
new file mode 100644
index 0000000000..2a2e970933
--- /dev/null
+++ b/ld/testsuite/ld-msp430-elf/location-3.s
@@ -0,0 +1,12 @@
+	.section	.faketext,"ax",%progbits
+	.global	loc9100_text
+	.type	loc9100_text, %function
+loc9100_text:
+	.word 0
+	.location 0x9100
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
index 875b413c14..22d476a4a5 100644
--- a/ld/testsuite/ld-msp430-elf/msp430-elf.exp
+++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
@@ -190,6 +190,10 @@ run_ld_link_tests $msp430eithershuffletests
 run_ld_link_tests $msp430warntests
 
 run_dump_test valid-map
+run_dump_test location-1
+run_dump_test location-2
+run_dump_test location-3
+
 run_ld_link_tests {{ "Check no reloc overflow with #lo and data in the upper region"
         "-m msp430X" "" "" {reloc-lo-430x.s} {} "reloc-lo-430x"}}
 run_ld_link_tests {{ "Check .upper prefixed input sections can be placed"
diff --git a/ld/testsuite/ld-x86-64/abind-1.d b/ld/testsuite/ld-x86-64/abind-1.d
new file mode 100644
index 0000000000..33e91e7c51
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/abind-1.d
@@ -0,0 +1,54 @@
+#name: SHF_GNU_ABIND 1 (using default linker script)
+#source: abind-1.s
+#ld: -e _start
+#notarget: ![supports_gnu_osabi]
+#nm: -n
+
+#...
+0+400000 t addr_0x400000_size_0x1000
+#...
+[0-9a-f]+ . text_size_0x1
+[0-9a-f]+ . text_size_0x10
+[0-9a-f]+ . text_size_0x80
+[0-9a-f]+ . text_size_0x100
+[0-9a-f]+ . text_size_0x800
+[0-9a-f]+ . text_size_0x1000
+#...
+[0-9a-f]+ T _start
+[0-9a-f]+ T __run_gnu_abind_init_array
+#...
+0+404002 r addr_0x404002
+0+406100 t addr_0x406100
+#...
+[0-9a-f]+ . rodata_size_0x1
+[0-9a-f]+ . rodata_size_0x10
+[0-9a-f]+ . rodata_size_0x80
+[0-9a-f]+ . rodata_size_0x100
+[0-9a-f]+ . rodata_size_0x800
+[0-9a-f]+ . rodata_size_0x1000
+#...
+0+40b200 r addr_0x40b200_size_0x1000
+#...
+0+40c402 d addr_0x40c402
+#...
+[0-9a-f]+ . data_size_0x1
+[0-9a-f]+ . data_size_0x10
+[0-9a-f]+ . data_size_0x80
+[0-9a-f]+ . data_size_0x100
+[0-9a-f]+ . data_size_0x800
+[0-9a-f]+ . data_size_0x1000
+#...
+0+40e000 b addr_0x40e000_size_0x1000
+#...
+0+40f000 b addr_0x40f000
+#...
+[0-9a-f]+ . __bss_start
+[0-9a-f]+ . _edata
+#...
+[0-9a-f]+ . bss_size_0x1
+[0-9a-f]+ . bss_size_0x10
+[0-9a-f]+ . bss_size_0x80
+[0-9a-f]+ . bss_size_0x100
+[0-9a-f]+ . bss_size_0x800
+[0-9a-f]+ . bss_size_0x1000
+#pass
diff --git a/ld/testsuite/ld-x86-64/abind-1.s b/ld/testsuite/ld-x86-64/abind-1.s
new file mode 100644
index 0000000000..c7068c7581
--- /dev/null
+++ b/ld/testsuite/ld-x86-64/abind-1.s
@@ -0,0 +1,175 @@
+/* Arbitrary sized .data sections. */
+	.section	.data.size_0x1,"aw"
+	.type	data_size_0x1, %object
+data_size_0x1:
+	.zero	0x1
+
+	.section	.data.size_0x10,"aw"
+	.type	data_size_0x10, %object
+data_size_0x10:
+	.zero	0x10
+
+	.section	.data.size_0x80,"aw"
+	.type	data_size_0x80, %object
+data_size_0x80:
+	.zero	0x80
+
+	.section	.data.size_0x100,"aw"
+	.type	data_size_0x100, %object
+data_size_0x100:
+	.zero	0x100
+
+	.section	.data.size_0x800,"aw"
+	.type	data_size_0x800, %object
+data_size_0x800:
+	.zero	0x800
+
+	.section	.data.size_0x1000,"aw"
+	.type	data_size_0x1000, %object
+data_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .bss sections.  */
+
+	.section	.bss.size_0x1,"aw",%nobits
+	.type	bss_size_0x1, %object
+bss_size_0x1:
+	.zero	0x1
+
+	.section	.bss.size_0x10,"aw",%nobits
+	.type	bss_size_0x10, %object
+bss_size_0x10:
+	.zero	0x10
+
+	.section	.bss.size_0x80,"aw",%nobits
+	.type	bss_size_0x80, %object
+bss_size_0x80:
+	.zero	0x80
+
+	.section	.bss.size_0x100,"aw",%nobits
+	.type	bss_size_0x100, %object
+bss_size_0x100:
+	.zero	0x100
+
+	.section	.bss.size_0x800,"aw",%nobits
+	.type	bss_size_0x800, %object
+bss_size_0x800:
+	.zero	0x800
+
+	.section	.bss.size_0x1000,"aw",%nobits
+	.type	bss_size_0x1000, %object
+bss_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .text sections.  */
+
+	.section	.text.size_0x1,"ax",%progbits
+	.type	text_size_0x1, %function
+text_size_0x1:
+	.zero	0x1
+
+	.section	.text.size_0x10,"ax",%progbits
+	.type	text_size_0x10, %function
+text_size_0x10:
+	.zero	0x10
+
+	.section	.text.size_0x80,"ax",%progbits
+	.type	text_size_0x80, %function
+text_size_0x80:
+	.zero	0x80
+
+	.section	.text.size_0x100,"ax",%progbits
+	.type	text_size_0x100, %function
+text_size_0x100:
+	.zero	0x100
+
+	.section	.text.size_0x800,"ax",%progbits
+	.type	text_size_0x800, %function
+text_size_0x800:
+	.zero	0x800
+
+	.section	.text.size_0x1000,"ax",%progbits
+	.type	text_size_0x1000, %function
+text_size_0x1000:
+	.zero	0x1000
+
+/* Arbitrary sized .rodata sections.  */
+
+	.section	.rodata.size_0x1,"a"
+	.type	rodata_size_0x1, %object
+rodata_size_0x1:
+	.zero	0x1
+
+	.section	.rodata.size_0x10,"a"
+	.type	rodata_size_0x10, %object
+rodata_size_0x10:
+	.zero	0x10
+
+	.section	.rodata.size_0x80,"a"
+	.type	rodata_size_0x80, %object
+rodata_size_0x80:
+	.zero	0x80
+
+	.section	.rodata.size_0x100,"a"
+	.type	rodata_size_0x100, %object
+rodata_size_0x100:
+	.zero	0x100
+
+	.section	.rodata.size_0x800,"a"
+	.type	rodata_size_0x800, %object
+rodata_size_0x800:
+	.zero	0x800
+
+	.section	.rodata.size_0x1000,"a"
+	.type	rodata_size_0x1000, %object
+rodata_size_0x1000:
+	.zero	0x1000
+
+/* Start .gnu.abind sections.  */
+
+	.section	.text.addr_0x400000,"axA",%progbits,0x400000
+	.type	addr_0x400000, %function
+addr_0x400000_size_0x1000:
+	.zero 0x1000
+
+	.section	.rodata.addr_0x404002,"aA",0x404002
+	.type	addr_0x404002, %object
+addr_0x404002:
+	.byte	2
+
+	.section	.text.addr_0x406100,"axA",%progbits,0x406100
+	.type	addr_0x406100, %function
+addr_0x406100:
+	.zero 2
+
+	.section	.rodata.addr_0x40b200,"aA",0x40b200
+	.type	addr_0x40b200, %object
+addr_0x40b200_size_0x1000:
+	.zero 0x1000
+
+	.section	.data.addr_0x40c402,"awA",0x40c402
+	.type	addr_0x40c402, %object
+addr_0x40c402:
+	.zero 2
+
+	.section	.bss.addr_0x40e000,"awA",%nobits,0x40e000
+	.type	addr_0x40e000, %object
+addr_0x40e000_size_0x1000:
+	.zero 0x1000
+
+	.section	.bss.addr_0x40f000,"awA",%nobits,0x40f000
+	.type	addr_0x40f000, %object
+addr_0x40f000:
+	.zero	1
+
+	.section	.text._start,"ax",%progbits
+	.global	_start
+	.type	_start, %function
+_start:
+	.word 0
+
+	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
+	.global	__run_gnu_abind_init_array
+	.type	__run_gnu_abind_init_array, %function
+__run_gnu_abind_init_array:
+	.word 0
diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
index 59cad54a79..5f61135ccb 100644
--- a/ld/testsuite/ld-x86-64/x86-64.exp
+++ b/ld/testsuite/ld-x86-64/x86-64.exp
@@ -245,6 +245,7 @@ if { ![ld_link $ld tmpdir/$test "-m$emul tmpdir/${test}a.o tmpdir/${test}b.o"] }
     }
 }
 
+run_dump_test "abind-1"
 run_dump_test "abs"
 run_dump_test "abs-k1om"
 run_dump_test "abs-l1om"
-- 
2.28.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [RFC] SHF_GNU_ABIND Section Flag
  2020-10-20 10:05 [RFC] SHF_GNU_ABIND Section Flag Jozef Lawrynowicz
@ 2020-10-23 10:20 ` Jozef Lawrynowicz
  0 siblings, 0 replies; 2+ messages in thread
From: Jozef Lawrynowicz @ 2020-10-23 10:20 UTC (permalink / raw)
  To: gnu-gabi

On Tue, Oct 20, 2020 at 11:05:57AM +0100, Jozef Lawrynowicz wrote:
> Hi,
> 
> I'd like to propose a new section flag, SHF_GNU_ABIND.
> 
> The flag allows the virtual memory address of a section to be specified
> within relocatable files, in advance of the final link. When linking
> executable files, the linker will try to place sections marked with
> SHF_GNU_ABIND at the address specified by their sh_addr field.
> 
> "ABIND" is short for "address bind", used to indicate that the
> section is "bound" to a specific address at run time.
> 
> I've attached a Binutils patch that implements SHF_GNU_ABIND.
> 
> I look forward to hearing your thoughts,
> Jozef


Does anyone have any comments on this proposal?

I was in two minds about including the .gnu.abind_init_array stuff, it
is probably superfluous as it is not required for the main use cases of
SHF_GNU_ABIND, so I think I will remove it. Most targets would not
require it at all.
Removing it will greatly simplify the proposal and keep any ABI
modifications to a minimum.

The real use cases for SHF_GNU_ABIND do not have to be concerned
with the case where LMA != VMA for the section being placed. Since they
are normally making use of a feature of the hardware located at that
address, that area of memory will be directly loadable.

The issue of .bss sections being placed outside the the block of memory
that is automatically initialized to zero by the startup code is easily
worked around by forcing the compiler to emit any zero initialized or
uninitialized data into a .data SHT_PROGBITS section.

Targets that want to support placement of LMA != VMA sections can
implement their own mechanism for handling it, as required. Meanwhile
the linker will just error if it encounters a GNU_ABIND section that
requires LMA != VMA.

So the only ABI changes would be to support the new section flag itself,
as defined below. If there ends up being some need for standardization
of .gnu.abind_init_array-like functionality, there is no reason that
can't be added later.

Thanks,
Jozef

> 
> ===========================================================
> -----------------------
> Section Attribute Flags
> -----------------------
> +-------------------------------------+
> | Name           | Value              |
> +-------------------------------------+
> | SHF_GNU_ABIND  | 0x400000 (1 << 22) |
> +-------------------------------------+
> 
> SHF_GNU_ABIND
>   The sh_addr field describes the virtual address at which
>   the first byte of the section should reside at in memory.
> 
> ===========================================================
> 
> Outline:
>   - Motivation
>   - Placement using linker script
>   - ABI details for SHF_GNU_ABIND runtime initialization
>   - Implementation
>   - Alternative Method to Implement the "location" attribute
>   - Conclusion
>   - Appendix - Examples
> 
> ----------
> Motivation
> ----------
> Placement of function or variable declarations at specific addresses can
> be useful in a variety of situations.
> 
> Many processors have areas of the memory map with special properties.
> The data stored in these areas may be treated by the hardware in
> some unique way.
> For example:
> - Slots in an interrupt vector table
>   When an interrupt triggers, the processor will read the address of the
>   associated interrupt service routine from a particular address within
>   the vector table.
> - Memory-mapped peripheral registers
>   To check the status, or modify the behavior, of peripherals, control
>   registers are defined at specific addresses and can be read from, or
>   written to.
> 
> Programmers may also find it useful to store information at a
> memorable address. Perhaps some data is shared between a bootloader and
> the application itself, stored in a common area of memory.
> 
> The requirement for SHF_GNU_ABIND is motivated by a broader proposal for
> a new attribute, "location", which can be set on function and variable
> declarations in the source code.
> This attribute will enable a declaration to be placed at a specific
> virtual address, and SHF_GNU_ABIND is used to convey this requirement in
> relocatable files.
> 
> Similar attributes are implemented in other toolchains, such as ARM
> Keil's "at" attribute, and the "location" attribute implemented in TI
> toolchains across a variety of their processors.
> 
> -----------------------------
> Placement using linker script
> -----------------------------
> To place a declaration of a function or variable at a specific address,
> the programmer must currently make modifications to the linker script.
> Linker script modifications for this are cumbersome, and must be coupled
> with modifications to the source code that place the declaration in a
> specific section.
> 
> The programmer has to:
> - Apply the "section" attribute to the declaration they want to place
>   at a specific address, so it gets placed in a uniquely named section.
> - Modify the linker script, creating a new output section with
>   the desired VMA for the declaration, containing a rule to match the
>   section they created for the declaration in the source code.
>  
> However, there are some potential pitfalls the programmer can fall into
> when making linker script modifications to place their section at
> a specific address:
> - The programmer may not consider the best position to put their new
>   output section within the list of output sections.
>   In a linker script with multiple output sections, a sub-optimal
>   decision about which sections they will place their new section
>   between could result in large, empty gaps in their executable file.
>   This limits the space available for their application, possibly
>   resulting in the program failing to link, or heap/stack availability
>   being low.
>   * With SHF_GNU_ABIND, the optimal position of the new output section
>     within the statement list can be automatically calculated.
> - The programmer may not consider whether an LMA region needs to be set
>   for their new output section, if their desired address is within a
>   non-loadable memory region.
>   * With SHF_GNU_ABIND, the linker automatically decides if a separate
>     LMA region is required and sets the load address accordingly.
> 
> There are additional generic benefits to avoiding linker script
> modifications:
> - The requirement for placement of a declaration at a specific address
>   may be application-specific, so consolidating this requirement to be
>   contained entirely within the source code improves portability.
> - Generally avoiding linker script modifications can improve the user
>   experience, when the programmer is inexperienced with the linker
>   script format.  The boilerplate linker script code required for
>   standard application operation can make modifications error-prone and
>   have unintended side-effects.
> 
> Furthermore, with SHF_GNU_ABIND, the number of individual changes
> required to achieve placement at a specific address is reduced.
> 
> With linker script modifications:
>   *.ld:
> | SECTIONS
> | {
> |   ... other sections ...
> |   my_decl 0x1000 : { *(.data.my_decl_at_0x1000) }
> |   ... other sections ...
> | }
>   *.c:
> | int __attribute__((section(".data.my_decl_at_0x1000"))) = 0xABCD;
> 
> With SHF_GNU_ABIND and the "location" attribute:
>   *.c:
> | int __attribute__((location(0x1000))) = 0xABCD;
> 
> Following are some further ABI details, describing the implementation
> required to support placement of sections with LMA not equal to VMA, and
> sections consisting of zero-initialized data.
> 
> ================================================================
> ----------------
> Special Sections
> ----------------
> .gnu.abind_init_array
>   This section holds an array of ElfXX_AbindInitArray_Entry entries,
>   which describe how to initialize the contents of SHF_GNU_ABIND
>   sections at runtime, for sections that require runtime initialization.
>   See "SHF_GNU_ABIND Section Initialization" for more details.
> 
> ------------------------------------
> SHF_GNU_ABIND Section Initialization
> ------------------------------------
> Some SHF_GNU_ABIND sections will require initialization at runtime. The
> conditions for the initialization requirement are:
> - The program will run on a ROM based system, so SHF_GNU_ABIND sections
>   with their virtual memory address (VMA) specified in a non-loadable
>   memory region require their contents to be copied from their load
>   memory address (LMA) to their VMA during program initialization.
> - The SHF_GNU_ABIND section has type SHT_NOBITS and would have been
>   placed in a .bss output section, and its contents set to zero during
>   program initialization. The specified VMA for the SHF_GNU_ABIND
>   section may not be within the bounds of the zero-initialized block of
>   other .bss sections, so the section will require it's contents to be
>   explicitly set to zero at runtime.  For targets that do not zero .bss,
>   this is not required.
> 
> For devices without non-loadable memory regions, and so will not have
> any sections with LMA not equal to VMA, it is possible to avoid the need
> for .gnu.abind_init_array.
> Defining any SHF_GNU_ABIND sections containing zero-initialized data
> explicitly as an SHT_PROGBITS ".data" section and initializing its
> contents to 0 will ensure the program loader sets its contents to 0,
> rather than relying on the application to zero it.
> .gnu.abind_init_array will therefore not be required in this case.
> If the compiler handles this automatically, .gnu.abind_init_array
> support could be entirely omitted for the target.
> 
> The "__run_gnu_abind_init_array" function performs runtime
> initialization of SHF_GNU_ABIND sections that require it, as described
> by .gnu.abind_init_array entries.
> 
> If .gnu.abind_init_array will be present in the executable file and has
> non-zero size, an entry for __run_gnu_abind_init_array must be placed in
> the ".init_array" section.
> 
> -----------------------------
> .gnu.abind_init_array Entries
> -----------------------------
> typedef struct
> {
>   Elf32_Addr vma;
>   Elf32_Addr lma;
>   Elf32_Word size;
> } Elf32_AbindInitArray_Entry;
> 
> typedef struct
> {
>   Elf64_Addr vma;
>   Elf64_Addr lma;
>   Elf64_Xword size;
> } Elf64_AbindInitArray_Entry;
> 
> The bounds of .gnu.abind_init_array are described by the following
> symbols:
>   __gnu_abind_init_array_start
>   __gnu_abind_init_array_end
> 
> To initialize the sections, __run_gnu_abind_init_array has a choice for
> each entry:
> - When lma != vma, copy "size" bytes from address "lma" to address
>   "vma".
> - When lma == vma, this is an SHT_NOBITS section requiring
>   zero-initialization. Set "size" bytes starting from address "vma" to
>   0.
>     
> A sample implementation of __run_gnu_abind_init_array for a 32-bit
> target is shown below:
> 
>   void
>   __run_gnu_abind_init_array (void)
>   {
>     void *ptr;
>     for (ptr = &__gnu_abind_init_array_start;
>          ptr !=  &__gnu_abind_init_array_end;
>          ptr += sizeof (Elf32_AbindInitArray_Entry))
>       {
>         Elf32_AbindInitArray_Entry *ent = ptr;
>          
>         if (ent->lma != ent->vma)
>           __builtin_memcpy (ent->vma, ent->lma, ent->size);
>         else
>           __builtin_memset (ent->vma, 0, ent->size);
>       }
>   }
> ================================================================
> --------------
> Implementation
> --------------
> GCC supports a new "location" attribute, which can be applied to
> function and variable declarations. The argument to the "location"
> attribute indicates the virtual memory address that the declaration
> should be placed at.
> 
>   int __attribute__((location(0x5000))) checksum = 0xABCD;
> 
> GCC will place the declaration that the "location" attribute is applied
> to in its own, uniquely named section. GCC will then describe the
> requirement for placement at a specific address to the assembler.
> 
> For systems which do not have any non-loadable memory regions, GCC can
> modify the type and name created for zero-initialized data. Placing this
> data in an SHT_PROGBITS section with the ".data" prefix, instead of an
> SHT_NOBITS sections with the ".bss" prefix, and explicitly initializing
> its contents to 0 will avoid the need for any runtime support for
> SHF_GNU_ABIND via .gnu.abind_init_array.
> 
> GAS supports a new flag value "A" to the "flags" argument of the
> .section directive. The specified address is read from the
> "flag_specific_arguments" argument to .section.
> 
>   .section .data.checksum,"awA",0x5000
> 
> GAS will set the SHF_GNU_ABIND flag on the section, and the sh_addr
> field to the specified address.
> 
> When loading object files to link an executable file, LD will "orphan"
> SHF_GNU_ABIND sections by renaming them, giving them the ".gnu.abind"
> prefix. This means they won't match any input section rules from the
> linker script. The "orphaned" SHF_GNU_ABIND input sections will now have
> their own output sections, which can be placed freely at the address
> specified by the sh_addr field of the corresponding input section.
> 
> --------------------------------------------------------
> Alternative Method to Implement the "location" attribute
> --------------------------------------------------------
> An alternative method of conveying the requirement for placement of a
> section at a specific address, from the compiler to the linker, would be
> for the compiler to set a specific section name which captures both the
> status of the section as one requiring placement, and the address it
> should be placed at.
> 
> For example:
>   int __attribute__((location(0x5000))) checksum = 0xABCD;
> becomes
>   .section .gnu_location_0x5000.data.checksum,"aw"
> 
> In theory, the assembler and linker implementation does not need to be
> very different from what is required for SHF_GNU_ABIND, as these
> programs can set an internal flag on sections which have the
> .gnu_location prefix to treat them in a special way.
> 
> However, the additional prefix for the section on entry to the assembler
> and linker might interfere with some common behavior.
> 
> It is also not standard to convey information about a section
> in the name.  This method should only be used if the proposed ABI
> modifications cannot be made for some reason.
> Additionally, it would result in the loss of opportunity for
> standardization of .gnu.abind_init_array and associated runtime
> functionality.
> 
> ----------
> Conclusion
> ----------
> The "location" attribute, making use of SHF_GNU_ABIND, will improve the
> user experience and simplify development for programmers wishing to make
> use of the functionality to place function and variable declarations at
> specific addresses. The behavior can now be implemented in fewer steps,
> and without leaving the source code. Cumbersome and error-prone linker
> script modifications are avoided.
> 
> For embedded devices, maintenance of board support packages is eased, as
> many hardware features that had to be described in both header file and
> linker scripts, with the corresponding definitions requiring precise
> alignment between the files, now require specification only in header
> files.
> 
> Applications are more portable, as those which require declarations to
> be placed at a specific address can now be entirely described from the
> source code, without requiring an accompanying linker script.
> 
> -------------------
> Appendix - Examples
> -------------------
> Below are some examples for the "location" attribute in real use cases.
> 
> Mapping a Structure over Memory-mapped Peripheral Registers
> ---------------------------------------------------------
> Current methods for accessing memory-mapped registers are:
> - Set the address of a structure at runtime, in application code.
>   * Requires additional code to setup the address of the structure
>     before registers can be accessed.
> - Use a symbolic reference to the registers, defined in header files but
>   resolved at link time.
>   * Requires implementation in both header file and linker scripts.
> 
> The "location" attribute allows consolidation of this information
> entirely within the source code, and without any runtime initialization
> required.
> 
>   typedef struct
>   {
>     char regA;
>     char regB;
>     char ctrl;
>   } peripheral_reg;
> 
>   peripheral_reg __attribute__((location(0x65000))) pregs1;
> 
>   /* Above code can be hidden in a header file from a board support
>      package for a particular device.  */
> 
>   void
>   manip_regs (void)
>   {
>     pregs1.ctrl = 1;        /* MOV  #1, &0x65002 */
>     pregs1.regA = 10;       /* MOV  #10, &0x65000 */
>     pregs1.regB = 20;       /* MOV  #20, &0x65001 */
>     pregs1.ctrl = 0;        /* MOV  #0, &0x65002 */
>   }
> 
> Placement in the Interrupt Vector Table
> ---------------------------------------
> Implementation of interrupt vectors currently requires explicit support
> between the source code and linker script:
> 
>   SECTIONS
>   {
>     __ivec_rtc 0xFF00 : { KEEP (*(__ivec_rtc)) }
>   }
> 
>   /* "interrupt" attribute is required to create the "__ivec_rtc"
>      section and place the address of "isr_rtc" within it.  */
>   void __attribute__((interrupt("rtc"))
>   isr_rtc (void)
>   {
>     clock_var++;
>   }
> 
> The name of the interrupt service routine must be aligned between the
> source code, compiler (a hard-coded prefix must be added) and the linker
> script.
> 
> The "location" attribute, used in conjunction with "retain" greatly
> simplifies the procedure, and does not require any linker script
> modifications.
> 
> /* If a board support package is available, refer to the address
>    symbolically.  */
> #ifdef __BSP__
>   static void * __attribute__((retain,location(VEC_RTC)))
> #else
>   static void * __attribute__((retain,location(0xFF00)))
> #endif
>     __ivec_rtc = isr_rtc;
> 
>   void
>   isr_rtc (void)
>   {
>     clock_var++;
>   }
> 
> As an extension to the proposal, a new "interrupt_loc" attribute could
> emit section declarations for vector table slots, setting SHF_GNU_RETAIN
> and SHF_GNU_ABIND on the section. This would allow an ISR to be
> completely specified with only a function declaration and possibly an
> associated definition from a header file.
> 
> #ifdef __BSP__
>   void __attribute__((interrupt_loc(VEC_RTC)))
> #else
>   void __attribute__((interrupt_loc(0xFF00)))
> #endif
>   isr_rtc (void)
>   {
>     clock_var++;
>   }

> From 5ed0fbad2b15c1a0419c4046fc11488811c68152 Mon Sep 17 00:00:00 2001
> From: Jozef Lawrynowicz <jozef.l@mittosystems.com>
> Date: Tue, 20 Oct 2020 10:44:37 +0100
> Subject: [PATCH] Implement SHF_GNU_ABIND
> 
> ---
>  bfd/elf-bfd.h                             |  10 +-
>  bfd/elf.c                                 |   9 +-
>  bfd/elflink.c                             | 133 ++++-
>  binutils/readelf.c                        |  10 +
>  gas/config/obj-elf.c                      |  47 +-
>  gas/config/obj-elf.h                      |   1 +
>  gas/testsuite/gas/elf/section10.d         |   4 +-
>  gas/testsuite/gas/elf/section10.s         |   4 +-
>  include/elf/common.h                      |   2 +
>  include/elf/external.h                    |  14 +
>  ld/configure.tgt                          |   1 -
>  ld/ldelfgen.c                             | 568 ++++++++++++++++++++++
>  ld/ldlang.c                               |   8 +-
>  ld/ldlang.h                               |   5 +
>  ld/testsuite/ld-arm/abind-1.d             |  49 ++
>  ld/testsuite/ld-arm/abind-1.s             | 183 +++++++
>  ld/testsuite/ld-arm/arm-elf.exp           |   2 +
>  ld/testsuite/ld-elf/abind-1.s             |  57 +++
>  ld/testsuite/ld-elf/abind-1a.d            |  27 +
>  ld/testsuite/ld-elf/abind-1a.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-1b.d            |  27 +
>  ld/testsuite/ld-elf/abind-1b.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-1c.d            |  27 +
>  ld/testsuite/ld-elf/abind-1c.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-1d.d            |  27 +
>  ld/testsuite/ld-elf/abind-1d.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-1e.d            |  27 +
>  ld/testsuite/ld-elf/abind-1e.ld           |  27 +
>  ld/testsuite/ld-elf/abind-1f.d            |  27 +
>  ld/testsuite/ld-elf/abind-1f.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-1g.d            |  27 +
>  ld/testsuite/ld-elf/abind-1g.ld           |  27 +
>  ld/testsuite/ld-elf/abind-1h.d            |  27 +
>  ld/testsuite/ld-elf/abind-1h.ld           |  27 +
>  ld/testsuite/ld-elf/abind-1i.d            |  27 +
>  ld/testsuite/ld-elf/abind-1i.ld           |  22 +
>  ld/testsuite/ld-elf/abind-2.s             | 183 +++++++
>  ld/testsuite/ld-elf/abind-2a.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2a.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-2b.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2b.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-2c.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2c.ld           |  27 +
>  ld/testsuite/ld-elf/abind-2d.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2d.ld           |  28 ++
>  ld/testsuite/ld-elf/abind-2e.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2e.ld           |  27 +
>  ld/testsuite/ld-elf/abind-2f.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2f.ld           |  27 +
>  ld/testsuite/ld-elf/abind-2g.d            |  73 +++
>  ld/testsuite/ld-elf/abind-2g.ld           |  22 +
>  ld/testsuite/ld-msp430-elf/location-1.d   |  23 +
>  ld/testsuite/ld-msp430-elf/location-1.s   |  98 ++++
>  ld/testsuite/ld-msp430-elf/location-2.d   |  30 ++
>  ld/testsuite/ld-msp430-elf/location-2.s   |  76 +++
>  ld/testsuite/ld-msp430-elf/location-3.d   |  10 +
>  ld/testsuite/ld-msp430-elf/location-3.s   |  12 +
>  ld/testsuite/ld-msp430-elf/msp430-elf.exp |   4 +
>  ld/testsuite/ld-x86-64/abind-1.d          |  54 ++
>  ld/testsuite/ld-x86-64/abind-1.s          | 175 +++++++
>  ld/testsuite/ld-x86-64/x86-64.exp         |   1 +
>  61 files changed, 2938 insertions(+), 19 deletions(-)
>  create mode 100644 ld/testsuite/ld-arm/abind-1.d
>  create mode 100644 ld/testsuite/ld-arm/abind-1.s
>  create mode 100644 ld/testsuite/ld-elf/abind-1.s
>  create mode 100644 ld/testsuite/ld-elf/abind-1a.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1a.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1b.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1b.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1c.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1c.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1d.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1d.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1e.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1e.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1f.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1f.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1g.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1g.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1h.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1h.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-1i.d
>  create mode 100644 ld/testsuite/ld-elf/abind-1i.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2.s
>  create mode 100644 ld/testsuite/ld-elf/abind-2a.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2a.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2b.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2b.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2c.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2c.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2d.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2d.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2e.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2e.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2f.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2f.ld
>  create mode 100644 ld/testsuite/ld-elf/abind-2g.d
>  create mode 100644 ld/testsuite/ld-elf/abind-2g.ld
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-1.d
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-1.s
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-2.d
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-2.s
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-3.d
>  create mode 100644 ld/testsuite/ld-msp430-elf/location-3.s
>  create mode 100644 ld/testsuite/ld-x86-64/abind-1.d
>  create mode 100644 ld/testsuite/ld-x86-64/abind-1.s
> 
> diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
> index ffb75f7919..1dc08ba2ba 100644
> --- a/bfd/elf-bfd.h
> +++ b/bfd/elf-bfd.h
> @@ -1897,15 +1897,17 @@ struct output_elf_obj_tdata
>    bfd_boolean flags_init;
>  };
>  
> -/* Indicate if the bfd contains SHF_GNU_MBIND/SHF_GNU_RETAIN sections or
> -   symbols that have the STT_GNU_IFUNC symbol type or STB_GNU_UNIQUE
> -   binding.  Used to set the osabi field in the ELF header structure.  */
> +/* Indicate if the bfd contains SHF_GNU_MBIND, SHF_GNU_RETAIN or SHF_GNU_ABIND
> +   sections or symbols that have the STT_GNU_IFUNC symbol type or
> +   STB_GNU_UNIQUE binding.  Used to set the osabi field in the ELF header
> +   structure.  */
>  enum elf_gnu_osabi
>  {
>    elf_gnu_osabi_mbind = 1 << 0,
>    elf_gnu_osabi_ifunc = 1 << 1,
>    elf_gnu_osabi_unique = 1 << 2,
>    elf_gnu_osabi_retain = 1 << 3,
> +  elf_gnu_osabi_abind = 1 << 4,
>  };
>  
>  typedef struct elf_section_list
> @@ -2035,7 +2037,7 @@ struct elf_obj_tdata
>    ENUM_BITFIELD (dynamic_lib_link_class) dyn_lib_class : 4;
>  
>    /* Whether the bfd uses OS specific bits that require ELFOSABI_GNU.  */
> -  ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 4;
> +  ENUM_BITFIELD (elf_gnu_osabi) has_gnu_osabi : 5;
>  
>    /* Whether if the bfd contains the GNU_PROPERTY_NO_COPY_ON_PROTECTED
>       property.  */
> diff --git a/bfd/elf.c b/bfd/elf.c
> index dc097e825a..5e30b1016a 100644
> --- a/bfd/elf.c
> +++ b/bfd/elf.c
> @@ -3284,8 +3284,13 @@ elf_fake_sections (bfd *abfd, asection *asect, void *fsarg)
>  
>    /* Don't clear sh_flags. Assembler may set additional bits.  */
>  
> -  if ((asect->flags & SEC_ALLOC) != 0
> -      || asect->user_set_vma)
> +  if (!arg->link_info
> +      && elf_section_flags (asect) & SHF_GNU_ABIND)
> +    {
> +      /* Don't clobber sh_addr already set for a GNU_ABIND section.  */
> +    }
> +  else if ((asect->flags & SEC_ALLOC) != 0
> +	   || asect->user_set_vma)
>      this_hdr->sh_addr = asect->vma * bfd_octets_per_byte (abfd, asect);
>    else
>      this_hdr->sh_addr = 0;
> diff --git a/bfd/elflink.c b/bfd/elflink.c
> index 742254055c..d93e744cf1 100644
> --- a/bfd/elflink.c
> +++ b/bfd/elflink.c
> @@ -4044,6 +4044,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
>    size_t tabsize = 0;
>    asection *s;
>    bfd_boolean just_syms;
> +  static bfd_boolean created_abind_init_sec = FALSE;
>  
>    htab = elf_hash_table (info);
>    bed = get_elf_backend_data (abfd);
> @@ -4081,16 +4082,16 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
>        (_("alternate ELF machine code found (%d) in %pB, expecting %d"),
>         ehdr->e_machine, abfd, bed->elf_machine_code);
>  
> -  /* As a GNU extension, any input sections which are named
> -     .gnu.warning.SYMBOL are treated as warning symbols for the given
> -     symbol.  This differs from .gnu.warning sections, which generate
> -     warnings when they are included in an output file.  */
> -  /* PR 12761: Also generate this warning when building shared libraries.  */
>    for (s = abfd->sections; s != NULL; s = s->next)
>      {
>        const char *name;
>  
>        name = bfd_section_name (s);
> +      /* As a GNU extension, any input sections which are named
> +	 .gnu.warning.SYMBOL are treated as warning symbols for the given
> +	 symbol.  This differs from .gnu.warning sections, which generate
> +	 warnings when they are included in an output file.  */
> +      /* PR 12761: Also generate this warning when building shared libraries.  */
>        if (CONST_STRNEQ (name, ".gnu.warning."))
>  	{
>  	  char *msg;
> @@ -4146,6 +4147,65 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
>  	      s->flags |= SEC_EXCLUDE;
>  	    }
>  	}
> +      else if (!bfd_link_relocatable (info)
> +	       && (elf_section_data (s)->this_hdr.sh_flags & SHF_GNU_ABIND))
> +	{
> +	  /* Orphan sections with the SHF_GNU_ABIND flag so they can be freely
> +	     moved around the statement list, by renaming them so they don't
> +	     match a linker script rule.  */
> +	  bfd_rename_section (s, concat (".gnu.abind", s->name, (const char *)NULL));
> +
> +	  if (!created_abind_init_sec)
> +	    {
> +	      /* Create .gnu.abind_init_array and an undefined symbol for the
> +		 library function required to make use of it.  */
> +	      asection *s_init;
> +	      flagword flags = (SEC_HAS_CONTENTS | SEC_READONLY
> +				| SEC_ALLOC | SEC_LOAD | SEC_KEEP);
> +
> +	      s_init = bfd_make_section_anyway_with_flags (abfd, ".gnu.abind_init_array", flags);
> +	      if (s_init == NULL)
> +		{
> +		  _bfd_error_handler
> +		    (_("%pB: could not create .gnu.abind_init_array section\n"), abfd);
> +		  goto error_return;
> +		}
> +
> +	      if (bed->s->arch_size == 64)
> +		{
> +		  elf_section_data (s_init)->this_hdr.sh_entsize
> +		    = sizeof (Elf64_External_AbindInitArray_Entry);
> +		  bfd_set_section_alignment (s_init, 8);
> +		}
> +	      else
> +		{
> +		  elf_section_data (s_init)->this_hdr.sh_entsize
> +		    = sizeof (Elf32_External_AbindInitArray_Entry);
> +		  bfd_set_section_alignment (s_init, 4);
> +		}
> +
> +	      /* Create an undefined symbol to ............. */
> +	      struct bfd_link_hash_entry *h;
> +
> +	      h = bfd_link_hash_lookup (info->hash, "__run_gnu_abind_init_array", TRUE, FALSE, TRUE);
> +	      if (h == NULL)
> +		{
> +		  _bfd_error_handler
> +		    (_("%pB: could not create __run_gnu_abind_init_array symbol\n"), abfd);
> +		  goto error_return;
> +		}
> +	      if (h->type == bfd_link_hash_new)
> +		{
> +		  h->type = bfd_link_hash_undefined;
> +		  h->u.undef.abfd = NULL;
> +		  h->non_ir_ref_regular = TRUE;
> +		  if (is_elf_hash_table (info->hash))
> +		    ((struct elf_link_hash_entry *) h)->mark = 1;
> +		  bfd_link_add_undef (info->hash, h);
> +		}
> +	      created_abind_init_sec = TRUE;
> +	    }
> +	}
>      }
>  
>    just_syms = ((s = abfd->sections) != NULL
> @@ -9801,6 +9861,66 @@ elf_link_output_symstrtab (struct elf_final_link_info *flinfo,
>    return 1;
>  }
>  
> +static bfd_boolean
> +_elf_link_swap_out_abind_init_array (bfd *abfd)
> +{
> +  asection *init_sec, *s;
> +  bfd_vma pos;
> +  const struct elf_backend_data *bed;
> +  int sizeof_addr;
> +
> +  init_sec = bfd_get_section_by_name (abfd, ".gnu.abind_init_array");
> +  if (init_sec == NULL)
> +    return TRUE;
> +
> +  bed = get_elf_backend_data (abfd);
> +
> +  if (bed->s->arch_size == 64)
> +    {
> +      elf_section_data (init_sec)->this_hdr.sh_entsize
> +	= sizeof (Elf64_External_AbindInitArray_Entry);
> +      sizeof_addr = 8;
> +    }
> +  else
> +    {
> +      elf_section_data (init_sec)->this_hdr.sh_entsize
> +	= sizeof (Elf32_External_AbindInitArray_Entry);
> +      sizeof_addr = 4;
> +    }
> +
> +  bfd_byte *data_contents = bfd_malloc (init_sec->size);
> +  memset (data_contents, 0, init_sec->size);
> +  pos = 0;
> +
> +  for (s = abfd->sections; s != NULL; s = s->next)
> +    {
> +      if ((elf_section_flags (s) & SHF_GNU_ABIND) == 0
> +	  /* .bss sections and those with LMA != VMA will require runtime
> +	     initialization using .gnu.abind_init_array.  */
> +	  || !(s->lma != s->vma
> +	       || (s->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
> +	continue;
> +
> +      if (pos >= init_sec->size)
> +	{
> +	  _bfd_error_handler(_("%pB: .gnu.abind_init_array has incorrect size\n"), abfd);
> +	  return FALSE;
> +	}
> +
> +      /* FIXME Use a "swap_out" function to write out the entry.  */
> +      /* When LMA == VMA, __run_gnu_abind_init_array knows the section is for
> +	 .bss and will instead zero-initialize for the given size.  */
> +      memcpy (data_contents + pos, &s->vma, sizeof_addr);
> +      pos += sizeof_addr;
> +      memcpy (data_contents + pos, &s->lma, sizeof_addr);
> +      pos += sizeof_addr;
> +      memcpy (data_contents + pos, &s->size, sizeof_addr);
> +      pos += sizeof_addr;
> +    }
> +  bfd_set_section_contents (abfd, init_sec, data_contents, 0, init_sec->size);
> +  return TRUE;
> +}
> +
>  /* Swap symbols out to the symbol table and flush the output symbols to
>     the file.  */
>  
> @@ -12823,6 +12943,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
>        goto return_local_hash_table;
>      }
>  
> +  /* Swap out .gnu.abind_init_array.  */
> +  _elf_link_swap_out_abind_init_array (abfd);
> +
>    /* Now we know the size of the symtab section.  */
>    if (bfd_get_symcount (abfd) > 0)
>      {
> diff --git a/binutils/readelf.c b/binutils/readelf.c
> index e6ec99a2cc..f43687c1ed 100644
> --- a/binutils/readelf.c
> +++ b/binutils/readelf.c
> @@ -5979,6 +5979,8 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
>        /* 25 */ { STRING_COMMA_LEN ("VLE") },
>        /* GNU specific.  */
>        /* 26 */ { STRING_COMMA_LEN ("GNU_RETAIN") },
> +      /* GNU specific.  */
> +      /* 27 */ { STRING_COMMA_LEN ("GNU_ABIND") },
>      };
>  
>    if (do_section_details)
> @@ -6070,6 +6072,8 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
>  		case ELFOSABI_FREEBSD:
>  		  if (flag == SHF_GNU_RETAIN)
>  		    sindex = 26;
> +		  else if (flag == SHF_GNU_ABIND)
> +		    sindex = 27;
>  		  /* Fall through */
>  		case ELFOSABI_NONE:
>  		  if (flag == SHF_GNU_MBIND)
> @@ -6148,6 +6152,12 @@ get_elf_section_flags (Filedata * filedata, bfd_vma sh_flags)
>  			  *p = 'R';
>  			  break;
>  			}
> +		      else if (flag == SHF_GNU_ABIND)
> +			{
> +			  *p = 'a';
> +			  break;
> +			}
> +
>  		      /* Fall through */
>  		    case ELFOSABI_NONE:
>  		      if (flag == SHF_GNU_MBIND)
> diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
> index 6586478975..2597b1018a 100644
> --- a/gas/config/obj-elf.c
> +++ b/gas/config/obj-elf.c
> @@ -741,6 +741,7 @@ obj_elf_change_section (const char *name,
>        elf_section_type (sec) = type;
>        elf_section_flags (sec) = attr;
>        elf_section_data (sec)->this_hdr.sh_info = match_p->sh_info;
> +      elf_section_data (sec)->this_hdr.sh_addr = match_p->sh_addr;
>  
>        /* Prevent SEC_HAS_CONTENTS from being inadvertently set.  */
>        if (type == SHT_NOBITS)
> @@ -801,7 +802,14 @@ obj_elf_change_section (const char *name,
>  		  | SEC_THREAD_LOCAL))
>  	      || ((elf_tdata (stdoutput)->has_gnu_osabi & elf_gnu_osabi_retain)
>  		  && ((elf_section_flags (old_sec) ^ match_p->sh_flags)
> -		      & SHF_GNU_RETAIN)))
> +		      & SHF_GNU_RETAIN))
> +	      /* Check GNU_ABIND state matches, and the address are the
> +		 same.  */
> +	      || ((elf_tdata (stdoutput)->has_gnu_osabi & elf_gnu_osabi_abind)
> +		  && (((elf_section_flags (old_sec) ^ match_p->sh_flags)
> +		      & SHF_GNU_ABIND)
> +		      || ((elf_section_data (old_sec)->this_hdr.sh_addr
> +			   != match_p->sh_addr)))))
>  	    {
>  	      if (ssect != NULL)
>  		as_warn (_("ignoring changed section attributes for %s"), name);
> @@ -867,6 +875,9 @@ obj_elf_parse_section_letters (char *str, size_t len,
>  	case 'R':
>  	  *gnu_attr |= SHF_GNU_RETAIN;
>  	  break;
> +	case 'A':
> +	  *gnu_attr |= SHF_GNU_ABIND;
> +	  break;
>  	case '?':
>  	  *is_clone = TRUE;
>  	  break;
> @@ -1332,6 +1343,36 @@ obj_elf_section (int push)
>  	  if ((gnu_attr & SHF_GNU_RETAIN) != 0)
>  	    match.sh_flags |= SHF_GNU_RETAIN;
>  
> +	  if ((gnu_attr & SHF_GNU_ABIND) != 0 && *input_line_pointer == ',')
> +	    {
> +	      char *save = input_line_pointer;
> +	      ++input_line_pointer;
> +	      SKIP_WHITESPACE ();
> +	      if (ISDIGIT (* input_line_pointer))
> +		{
> +		  char *t = input_line_pointer;
> +		  match.sh_addr = strtoul (input_line_pointer,
> +					   &input_line_pointer, 0);
> +		  if (match.sh_addr == (unsigned int) -1)
> +		    {
> +		      as_warn (_("invalid address for GNU_ABIND section: %s"), t);
> +		      match.sh_addr = 0;
> +		    }
> +		}
> +	      else
> +		{
> +		  as_warn (_("expected integer constant for GNU_ABIND section address"));
> +		  match.sh_addr = 0;
> +		  input_line_pointer = save;
> +		}
> +	    }
> +	  else if ((attr & SHF_GNU_ABIND) != 0)
> +	    {
> +	      as_warn (_("address for SHF_GNU_ABIND not specified"));
> +	      attr &= ~SHF_GNU_ABIND;
> +	    }
> +
> +
>  	  if (*input_line_pointer == ',')
>  	    {
>  	      char *save = input_line_pointer;
> @@ -1420,7 +1461,7 @@ obj_elf_section (int push)
>   done:
>    demand_empty_rest_of_line ();
>  
> -  if ((gnu_attr & (SHF_GNU_MBIND | SHF_GNU_RETAIN)) != 0)
> +  if ((gnu_attr & SHF_MASKOS) != 0)
>      {
>        struct elf_backend_data *bed;
>        bfd_boolean mbind_p = (gnu_attr & SHF_GNU_MBIND) != 0;
> @@ -1446,6 +1487,8 @@ obj_elf_section (int push)
>  	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_mbind;
>  	  if ((gnu_attr & SHF_GNU_RETAIN) != 0)
>  	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_retain;
> +	  if ((gnu_attr & SHF_GNU_ABIND) != 0)
> +	    elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_abind;
>  
>  	  attr |= gnu_attr;
>  	}
> diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
> index 0a91ed462f..c5492cc8bc 100644
> --- a/gas/config/obj-elf.h
> +++ b/gas/config/obj-elf.h
> @@ -108,6 +108,7 @@ struct elf_section_match
>    const char *   linked_to_symbol_name;
>    unsigned int   section_id;
>    unsigned int   sh_info;		/* ELF section information.  */
> +  bfd_vma	 sh_addr;		/* ELF section address.  */
>    bfd_vma	 sh_flags;		/* ELF section flags.  */
>    flagword       flags;
>  };
> diff --git a/gas/testsuite/gas/elf/section10.d b/gas/testsuite/gas/elf/section10.d
> index 6aa7b088b1..8346d7b13c 100644
> --- a/gas/testsuite/gas/elf/section10.d
> +++ b/gas/testsuite/gas/elf/section10.d
> @@ -18,7 +18,7 @@
>  #...
>  [ 	]*\[.*\][ 	]+sec3
>  [ 	]*PROGBITS.*
> -[ 	]*\[.*fedff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
> +[ 	]*\[.*fe9ff030\]: MERGE, STRINGS,.* EXCLUDE, OS \(.*e900000\), PROC \(.*[3467]0000000\), UNKNOWN \(0+0ff000\)
>  #...
>  [ 	]*\[.*\][ 	]+sec4
>  [ 	]*LOOS\+0x11[ 	].*
> @@ -26,7 +26,7 @@
>  #...
>  [ 	]*\[.*\][ 	]+sec5
>  [ 	]*LOUSER\+0x9[ 	].*
> -[ 	]*\[.*fedf0000\]:.* EXCLUDE, OS \(.*ed00000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
> +[ 	]*\[.*fe9f0000\]:.* EXCLUDE, OS \(.*e900000\), PROC \(.*[3467]0000000\), UNKNOWN \(.*f0000\)
>  [ 	]*\[.*\][ 	]+.data.foo
>  [ 	]*LOUSER\+0x7f000000[ 	].*
>  [ 	]*\[0+003\]: WRITE, ALLOC
> diff --git a/gas/testsuite/gas/elf/section10.s b/gas/testsuite/gas/elf/section10.s
> index d52b3458fb..1236045c3c 100644
> --- a/gas/testsuite/gas/elf/section10.s
> +++ b/gas/testsuite/gas/elf/section10.s
> @@ -7,7 +7,7 @@
>  	.word 2
>  
>  	# Make sure that specifying further arguments to .sections is still supported
> -	.section sec3, "0xfedff000MS", %progbits, 32
> +	.section sec3, "0xfe9ff000MS", %progbits, 32
>  	.word 3
>  
>  	# Make sure that extra flags can be set for well known sections as well.
> @@ -19,7 +19,7 @@
>  	.word 5
>  
>  	# Test both together, with a quoted type value.
> -	.section sec5, "0xfedf0000", "0x80000009"
> +	.section sec5, "0xfe9f0000", "0x80000009"
>  	.word 6
>  
>  	# Test that declaring an extended version of a known special section works.
> diff --git a/include/elf/common.h b/include/elf/common.h
> index c01e562c78..babff115d9 100644
> --- a/include/elf/common.h
> +++ b/include/elf/common.h
> @@ -555,6 +555,8 @@
>  #define SHF_MASKOS	0x0FF00000	/* New value, Oct 4, 1999 Draft */
>  #define SHF_GNU_BUILD_NOTE    (1 << 20)	/* Section contains GNU BUILD ATTRIBUTE notes.  */
>  #define SHF_GNU_RETAIN	      (1 << 21)	/* Section should not be garbage collected by the linker.  */
> +#define SHF_GNU_ABIND	      (1 << 22)	/* Section should placed at a specific VMA.  */
> +
>  #define SHF_MASKPROC	0xF0000000	/* Processor-specific semantics */
>  
>  /* This used to be implemented as a processor specific section flag.
> diff --git a/include/elf/external.h b/include/elf/external.h
> index 230fdabd87..6f6e678dcd 100644
> --- a/include/elf/external.h
> +++ b/include/elf/external.h
> @@ -311,6 +311,20 @@ typedef struct
>    unsigned char		a_val[8];
>  } Elf64_External_Auxv;
>  
> +typedef struct
> +{
> +  unsigned char vma[4];
> +  unsigned char lma[4];
> +  unsigned char size[4];
> +} Elf32_External_AbindInitArray_Entry;
> +
> +typedef struct
> +{
> +  unsigned char vma[8];
> +  unsigned char lma[8];
> +  unsigned char size[8];
> +} Elf64_External_AbindInitArray_Entry;
> +
>  /* Size of SHT_GROUP section entry.  */
>  
>  #define GRP_ENTRY_SIZE		4
> diff --git a/ld/configure.tgt b/ld/configure.tgt
> index 70359301b5..9db64ed12d 100644
> --- a/ld/configure.tgt
> +++ b/ld/configure.tgt
> @@ -584,7 +584,6 @@ moxie-*-*)		targ_emul=elf32moxie
>  			;;
>  msp430-*-*)		targ_emul=msp430elf
>  			targ_extra_emuls="msp430X"
> -			targ_extra_ofiles=ldelfgen.o
>  			;;
>  mt-*elf)		targ_emul=elf32mt
>  			targ_extra_ofiles=ldelfgen.o
> diff --git a/ld/ldelfgen.c b/ld/ldelfgen.c
> index 3a5619435c..c1b9f8c1ab 100644
> --- a/ld/ldelfgen.c
> +++ b/ld/ldelfgen.c
> @@ -30,11 +30,18 @@
>  #include "elf-bfd.h"
>  #include "ldelfgen.h"
>  
> +static bfd_boolean place_bound_sections (void);
> +static void dump_sections (lang_statement_list_type *root, lang_memory_region_type *r, int indent);
> +
> +
>  void
>  ldelf_map_segments (bfd_boolean need_layout)
>  {
>    int tries = 10;
>  
> +  if (!bfd_link_relocatable (&link_info))
> +    place_bound_sections ();
> +
>    do
>      {
>        lang_relax_sections (need_layout);
> @@ -211,3 +218,564 @@ extern void ldelf_examine_strtab_for_ctf
>     struct elf_strtab_hash *symstrtab ATTRIBUTE_UNUSED)
>  {}
>  #endif
> +
> +\f
> +/* Start SHF_GNU_ABIND handling.  */
> +
> +/* Insert __gnu_abind_init_array_{start,end} symbols at the start/end of
> +   .gnu.abind_init_array.  */
> +static bfd_boolean
> +insert_abind_init_array_symbols (void)
> +{
> +  lang_statement_union_type * curr;
> +
> +  lang_relax_sections (TRUE);
> +
> +  for (curr = stat_ptr->head; curr != NULL; curr = curr->header.next)
> +    {
> +      switch (curr->header.type)
> +	{
> +	case lang_output_section_statement_enum:
> +	  if (curr->output_section_statement.bfd_section
> +	      && strcmp (curr->output_section_statement.bfd_section->name,
> +			 ".gnu.abind_init_array") == 0)
> +	    {
> +	      asection *os = curr->output_section_statement.bfd_section;
> +	      /* FIXME this gets orphaned at the end of the map file would be
> +		 good to attach it for aesthetics.  */
> +	      lang_add_assignment (exp_provide ("__gnu_abind_init_array_start",
> +						exp_intop (os->vma), FALSE));
> +	      lang_add_assignment (exp_provide ("__gnu_abind_init_array_end",
> +						exp_intop (os->vma + os->size),
> +						FALSE));
> +	      return TRUE;
> +	    }
> +	  break;
> +	default:
> +	  break;
> +	}
> +    }
> +  return FALSE;
> +}
> +
> +/* Check a memory region exists at the address specified for the GNU_ABIND
> +   section, and that we can place the section in that region.  */
> +static lang_memory_region_type *
> +get_region_for_abind (asection *sec,
> +		      bfd_boolean validate_flags ATTRIBUTE_UNUSED)
> +{
> +  bfd_vma addr = sec->vma;
> +  lang_memory_region_type *r;
> +  lang_memory_region_type *ret = NULL;
> +
> +  for (r = get_memory_region_list (); r != NULL; r = r->next)
> +    {
> +      /* The default memory region spans the entire address space.  Use it if
> +	 no other memory region contains the address.  */
> +      if (strcmp ("*default*", r->name_list.name) == 0)
> +	ret = r;
> +      else if (addr >= r->origin && addr < (r->origin + r->length))
> +	{
> +	  /* FIXME/TODO Check the region we want to place the section in is
> +	     compatible with its type.  */
> +#if 0
> +	  if (validate_flags
> +	      && (!verify_region_compatibility (r, stat_ptr, sec->flags)))
> +	    {
> +	      einfo (_("%P: warning: GNU_ABIND section '%s' is not compatible "
> +		       "with the memory region '%s' containing address 0x%v\n"),
> +		     sec->name, r->name_list.name, addr);
> +	      return NULL;
> +	    }
> +#endif
> +	  return r;
> +	}
> +    }
> +  return ret;
> +}
> +
> +
> +/* Detach the output section S from the list that starts from ROOT.  */
> +static bfd_boolean
> +detach_output_sec (asection *s, lang_statement_list_type *root,
> +		   bfd_boolean dump)
> +{
> +  asection *os;
> +  lang_statement_union_type * prev = NULL;
> +  lang_statement_union_type * curr;
> +  lang_output_section_statement_type * os_stat;
> +
> +  for (curr = root->head; curr != NULL; curr = curr->header.next)
> +    {
> +      switch (curr->header.type)
> +	{
> +	case lang_output_section_statement_enum:
> +	  os_stat = &curr->output_section_statement;
> +	  if (!os_stat->bfd_section)
> +	    break;
> +
> +	  os = os_stat->bfd_section;
> +
> +	  if (os == s)
> +	    {
> +	      /* If this is the last statement, and we are detaching this
> +		 statement, we have to point the list tail to the previous
> +		 elements' next pointer.  */
> +	      if (curr->header.next == NULL)
> +		root->tail = &prev->header.next;
> +
> +	      /* Adjust the next statement pointed to by the previous element.  */
> +	      if (prev == NULL)
> +		root->head = curr->header.next;
> +	      else
> +		prev->header.next = curr->header.next;
> +
> +	      /* This statement is no longer part of any list, so clear the next
> +		 element it points to.  */
> +	      curr->header.next = NULL;
> +
> +	      lang_relax_sections (TRUE);
> +	      return TRUE;
> +	    }
> +	  break;
> +
> +	case lang_group_statement_enum:
> +	  if (detach_output_sec (s, &curr->group_statement.children, dump))
> +	    return TRUE;
> +	  break;
> +
> +	default:
> +	  break;
> +	}
> +      prev = curr;
> +    }
> +  return FALSE;
> +}
> +
> +static int
> +compare_abind_sec_vma (const void *a, const void *b)
> +{
> +  asection *asec = *(asection **) a, *bsec = *(asection **) b;
> +
> +  if (asec->vma > bsec->vma)
> +    return 1;
> +  else if (asec->vma < bsec->vma)
> +    return -1;
> +
> +  return 0;
> +}
> +
> +/* Create a list of GNU_ABIND sections, sorted in ascending order of
> +   their desired VMA.  */
> +static unsigned int
> +sort_abind_secs (asection ***sec_list_p)
> +{
> +  bfd *ibfd;
> +  asection *sec;
> +  asection **sec_list;
> +  unsigned int sec_count = 0;
> +  unsigned int list_size = 10;
> +
> +  sec_list = (asection **) xmalloc (list_size * sizeof (asection *));
> +  *sec_list_p = sec_list;
> +
> +  for (ibfd = link_info.input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
> +    for (sec = ibfd->sections; sec != NULL; sec = sec->next)
> +      {
> +	if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
> +	    || !(elf_section_flags (sec) & SHF_GNU_ABIND)
> +	    || (elf_section_flags (sec) & SEC_EXCLUDE)
> +	    || strcmp (sec->output_section->name, DISCARD_SECTION_NAME) == 0
> +	    || sec->output_section == bfd_abs_section_ptr
> +	    || (link_info.gc_sections && !sec->gc_mark))
> +	  continue;
> +
> +	/* FIXME add an option to disable placement of lma != vma bound sections.  */
> +	/*if (sec->output_section->lma != sec->output_section->vma)*/
> +	  /*continue;*/
> +
> +	/* Before we go any further, validate the address of section.
> +	   We may not be able to place it there at all.  */
> +	/*if (get_region_for_abind (sec, TRUE) == NULL)*/
> +	  /*continue;*/
> +
> +	if (sec_count == list_size)
> +	  {
> +	    list_size *= 2;
> +	    sec_list = (asection **)
> +	      xrealloc (sec_list, list_size * sizeof (asection *));
> +	  }
> +
> +	sec_list[sec_count++] = sec;
> +
> +	/* Sort the SHF_GNU_ABIND input sections by their specified VMA.  */
> +	qsort (sec_list, sec_count, sizeof (asection *), &compare_abind_sec_vma);
> +      }
> +  return sec_count;
> +}
> +
> +/* Initialize a new lang_statement_union_type with the given output section
> +   statement.  */
> +static lang_statement_union_type *
> +insert_os_stat (lang_output_section_statement_type *os_stat)
> +{
> +  lang_statement_union_type *new_stmt;
> +  new_stmt = stat_alloc (sizeof (lang_output_section_statement_type));
> +  new_stmt->header.type = lang_output_section_statement_enum;
> +
> +  new_stmt->output_section_statement = *os_stat;
> +  new_stmt->output_section_statement.addr_tree = NULL;
> +  new_stmt->header.next = NULL;
> +  return new_stmt;
> +}
> +
> +/* Initialize an output section with a wild statement.  */
> +static void
> +insert_wild (lang_statement_list_type *list)
> +{
> +  lang_wild_statement_type *wild = NULL;
> +
> +  wild = stat_alloc (sizeof (lang_wild_statement_type));
> +  wild->header.next = NULL;
> +  wild->header.type = lang_wild_statement_enum;
> +
> +  wild->filename = NULL;
> +  wild->filenames_sorted = FALSE;
> +  wild->section_flag_list = NULL;
> +  wild->exclude_name_list = NULL;
> +  wild->section_list = NULL;
> +  wild->keep_sections = FALSE;
> +  lang_list_init (&wild->children);
> +
> +  *(list->tail) = (void *)wild;
> +  list->tail = &wild->header.next;
> +
> +  lang_relax_sections (TRUE);
> +}
> +
> +/* Return TRUE if this is the last output section statement in the entire statement list.  */
> +static bfd_boolean
> +is_last_real_os (lang_statement_union_type *os_stat)
> +{
> +  lang_statement_union_type *curr;
> +  for (curr = os_stat->header.next; curr != NULL; curr = curr->header.next)
> +    {
> +      switch (curr->header.type)
> +	{
> +	case lang_output_section_statement_enum:
> +	  if (curr->output_section_statement.bfd_section
> +	      && (curr->output_section_statement.bfd_section->flags & SEC_ALLOC))
> +	    return FALSE;
> +	  break;
> +	default:
> +	  /* FIXME: Do we need to handle group statements?  */
> +	  break;
> +	}
> +    }
> +  return TRUE;
> +}
> +
> +
> +/* Place SEC before the output section os_stat. If os_stat is NULL, there
> +   should be free space at the desired address and we attach it to the
> +   statement list as required.  */
> +static lang_output_section_statement_type *
> +move_abind_os (asection *sec, lang_statement_list_type *root)
> +{
> +  lang_statement_union_type * curr;
> +  lang_statement_union_type * prev = NULL;
> +  lang_statement_union_type * prev_os_stat = NULL;
> +  lang_statement_union_type * insert_after = NULL;
> +  bfd_vma addr = sec->vma;
> +  lang_output_section_statement_type * os_stat;
> +  asection *os;
> +  const int dump = 0;
> +  if (dump)
> +    printf ("\nMoving %s\n", sec->name);
> +
> +  /* We detached the output section statements for all GNU_ABIND sections
> +     earlier.  Find the statement for this output section, and reattach it.  */
> +  os_stat = lang_output_section_find (sec->output_section->name);
> +  os_stat->region = get_region_for_abind (sec, FALSE);
> +
> +  /* Find the position in the statement list to insert this GNU_ABIND
> +     section.  */
> +  for (curr = root->head; curr != NULL; curr = curr->header.next)
> +    {
> +      switch (curr->header.type)
> +	{
> +	case lang_output_section_statement_enum:
> +	  if (curr->output_section_statement.bfd_section == NULL
> +	      || bfd_is_abs_section (curr->output_section_statement.bfd_section))
> +	    {
> +	      /* Always keep a record of the last output section statement, so a
> +		 GNU_ABIND section can be attached after it.  */
> +	      prev_os_stat = prev = curr;
> +	      continue;
> +	    }
> +
> +	  os = curr->output_section_statement.bfd_section;
> +	  if (dump)
> +	    printf ("  %s vma 0x%lx size 0x%lx\n", os->name,
> +		    os->vma, os->size);
> +
> +	  if (os->vma >= addr || (os->vma + os->size > addr))
> +	    {
> +	      /* Between the end of the prev_os_stat and the end of this current
> +		 output section is the desired VMA for the GNU_ABIND section.
> +		 Insert the GNU_ABIND section after prev_os_stat.
> +		 If there is no prev_os_stat insert at the first suitable place in the overall
> +		 statement list, using insert_os_after to find that place.  */
> +	      if (prev_os_stat == NULL)
> +		insert_after = *(insert_os_after ((lang_output_section_statement_type *)root->head));
> +	      else
> +		insert_after = prev_os_stat;
> +	    }
> +	  else if (curr->header.next == NULL || is_last_real_os (curr))
> +	    {
> +	      /* Insert at the end of the statement list/after the last output
> +		 section.  */
> +	      insert_after = curr;
> +	    }
> +
> +	  if (insert_after != NULL)
> +	    {
> +	      lang_statement_union_type *new_stmt = insert_os_stat (os_stat);
> +	      new_stmt->output_section_statement.addr_tree = exp_intop (addr);
> +
> +	      if (dump)
> +		{
> +		  if (insert_after->header.type == lang_output_section_statement_enum
> +		      && insert_after->output_section_statement.bfd_section)
> +		    printf ("  INSERT %s HERE, after %s\n", sec->name,
> +			    insert_after->output_section_statement.bfd_section->name);
> +		  else
> +		    printf ("  INSERT %s HERE, after unknown\n", sec->name);
> +		}
> +
> +	      new_stmt->header.next = insert_after->header.next;
> +	      insert_after->header.next = new_stmt;
> +
> +	      if (insert_after->header.next == NULL)
> +		root->tail = &new_stmt->header.next;
> +
> +	      return &new_stmt->output_section_statement;
> +	    }
> +	  prev_os_stat = curr;
> +	  break;
> +	default:
> +	  if (dump)
> +	    printf ("  statement type %d\n", curr->header.type);
> +	  break;
> +	}
> +      prev = curr;
> +    }
> +  return NULL;
> +}
> +
> +/* Place SHF_GNU_ABIND sections at their requested VMAs, if possible.  */
> +static bfd_boolean
> +place_bound_sections (void)
> +{
> +  bfd *ibfd;
> +  asection *sec;
> +  unsigned int num_abind;
> +  lang_output_section_statement_type *os_stat;
> +  unsigned int i;
> +  asection **abind_secs;
> +
> +  num_abind = sort_abind_secs (&abind_secs);
> +
> +  if (num_abind == 0)
> +    return TRUE;
> +
> +  /* Detach the GNU_ABIND sections from the global statement list.  This ensures that
> +     regular sections are in accurate positions each time we go to look at
> +     placing the GNU_ABIND section.
> +     Create .abind_*_init_array entries if required.  */
> +  for (i = 0; i < num_abind; i++)
> +    {
> +      static asection *init_sec = NULL;
> +      struct elf_link_hash_entry *h;
> +
> +      sec = abind_secs[i]->output_section;
> +
> +      /* .bss sections and those with LMA != VMA will require runtime
> +	 initialization using .gnu.abind_init_array.  */
> +      if ((sec->lma != sec->vma
> +	   || (sec->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
> +	{
> +	  if (init_sec == NULL)
> +	    {
> +	      asection *s;
> +	      /* Find the input section version of .gnu.abind_init_array.
> +		 Changes to the size of the output section don't persist.  */
> +	      for (ibfd = link_info.input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
> +		for (s = ibfd->sections; s != NULL; s = s->next)
> +		  if (strcmp (s->name, ".gnu.abind_init_array") == 0)
> +		    init_sec = s;
> +
> +	      /* .gnu.abind_init_array should have been created in
> +		 elf_link_add_object_symbols.  */
> +	      if (init_sec == NULL)
> +		{
> +		  einfo (_("%X%P: .gnu.abind_init_array has not been created\n"));
> +		  return FALSE;
> +		}
> +	    }
> +
> +	  /* If the section will require a .gnu.abind_init_array entry, then additional
> +	     support from the run time library is required.
> +	     Verify that support is available by checking the
> +	     __run_gnu_abind_init_array has been resolved.  */
> +	  h = elf_link_hash_lookup (elf_hash_table (&link_info),
> +				    "__run_gnu_abind_init_array",
> +				    FALSE, FALSE, FALSE);
> +
> +	  if (h == NULL
> +	      || (h != NULL && (h->root.type != bfd_link_hash_defined)))
> +	    {
> +	      /* If we could restore the .gnu.abind section to its original name
> +		 and place it in its original output section, maybe this
> +		 error wouldn't have to be fatal.  However, if the program requires
> +		 the section to be at a specific address, then it will not be
> +		 correct if we can't place the ABIND section.  */
> +	      einfo (_("%X%P: __run_gnu_abind_init_array symbol has not been resolved\n"));
> +
> +	      /* Report the remaining abind sections that can't be placed, then
> +		 quit.  */
> +	      for (; i < num_abind; i++)
> +		{
> +		  if (!(sec->lma != sec->vma
> +			|| (sec->flags & (SEC_DATA | SEC_LOAD | SEC_READONLY | SEC_CODE)) == 0))
> +		    continue;
> +		  elf_section_flags (abind_secs[i]) &= ~SHF_GNU_ABIND;
> +		  elf_section_flags (abind_secs[i]->output_section) &= ~SHF_GNU_ABIND;
> +		  einfo (_("%X%P: SHF_GNU_ABIND section %s requires "
> +			   "initialization from __run_gnu_abind_init_array\n"), abind_secs[i]->name);
> +		}
> +	      return FALSE;
> +	    }
> +	  bfd_set_section_size (init_sec, init_sec->size
> +				+ elf_section_data (init_sec)->this_hdr.sh_entsize);
> +	}
> +
> +      if (!detach_output_sec (abind_secs[i]->output_section, stat_ptr, FALSE))
> +	return FALSE;
> +    }
> +
> +
> +  for (i = 0; i < num_abind; i++)
> +    {
> +      lang_relax_sections (TRUE);
> +      sec = abind_secs[i];
> +      os_stat = move_abind_os (sec, stat_ptr);
> +
> +      if (os_stat == NULL)
> +	{
> +	  einfo (_("%P: error: unable to place GNU_ABIND section %s\n"), sec->name);
> +	  continue;
> +	}
> +
> +      /* Refresh this bound section, which should only
> +	 ever contain a SHF_GNU_ABIND input section.  */
> +      lang_list_init (&os_stat->children);
> +      insert_wild (&os_stat->children);
> +      /* We need to clear the output section before calling
> +	 lang_add_section.  */
> +      sec->output_section = NULL;
> +      lang_add_section (&os_stat->children.head->wild_statement.children,
> +			sec, NULL, os_stat);
> +    }
> +
> +  if (!insert_abind_init_array_symbols ())
> +    {
> +      einfo (_("%P: couldn't find .gnu.abind_init_array section to define "
> +	       "__gnu_abind_init_array_{start,end} symbols"));
> +      return FALSE;
> +    }
> +  return TRUE;
> +}
> +
> +static void ATTRIBUTE_UNUSED
> +dump_sections (lang_statement_list_type *root, lang_memory_region_type *r, int indent)
> +{
> +  lang_statement_union_type * curr;
> +  lang_output_section_statement_type * os_stat;
> +  asection *s;
> +  int i;
> +
> +  for (curr = root->head; curr != NULL; curr = curr->header.next)
> +    {
> +      switch (curr->header.type)
> +	{
> +	case lang_input_section_enum:
> +	  s = curr->input_section.section;
> +
> +	  for (i = 0; i < indent; i++)
> +	    printf ("  ");
> +	  if (s)
> +	    printf ("%s\n", s->name);
> +	  else
> +	    printf ("unknown input section\n");
> +
> +	  break;
> +
> +	case lang_output_section_statement_enum:
> +	  os_stat = &curr->output_section_statement;
> +	  for (i = 0; i < indent; i++)
> +	    printf ("  ");
> +	  if (r != os_stat->region)
> +	    {
> +	      r = os_stat->region;
> +	      if (r)
> +		printf ("\nNEW REGION %s\n---\n", r->name_list.name);
> +	      else
> +		printf ("\nNEW UNKNOWN REGION\n---\n");
> +	    }
> +	  s = curr->output_section_statement.bfd_section;
> +	  for (i = 0; i < indent; i++)
> +	    printf ("  ");
> +	  if (s)
> +	    {
> +	      printf ("%s LMA: 0x%lx VMA: 0x%lx", s->name, s->lma, s->vma);
> +	      if (os_stat->lma_region)
> +		printf (" LMA region = %s", os_stat->lma_region->name_list.name);
> +	      if (os_stat->region)
> +		printf (" VMA region = %s", os_stat->region->name_list.name);
> +	      printf ("\n");
> +	    }
> +	  else
> +	    printf ("unknown output section, curr region vma 0x%lx\n", os_stat->region->current);
> +
> +	  dump_sections (&curr->output_section_statement.children, curr->output_section_statement.region, indent + 1);
> +	  break;
> +
> +	case lang_wild_statement_enum:
> +	  for (i = 0; i < indent; i++)
> +	    printf ("  ");
> +	  printf ("wild statement\n");
> +	  dump_sections (&curr->wild_statement.children, r, indent + 1);
> +	  break;
> +
> +	case lang_group_statement_enum:
> +	  dump_sections (&curr->group_statement.children, r, indent + 1);
> +	  break;
> +
> +	case lang_assignment_statement_enum:
> +	  if (1)
> +	    {
> +	      lang_assignment_statement_type *ass;
> +
> +	      ass = &curr->assignment_statement;
> +	      for (i = 0; i < indent; i++)
> +		printf ("  ");
> +	      printf ("assignment statement, dst = %s\n", ass->exp->assign.dst);
> +	    }
> +	  break;
> +
> +	default:
> +	  break;
> +	}
> +    }
> +}
> diff --git a/ld/ldlang.c b/ld/ldlang.c
> index 4249b3a045..b8d260f780 100644
> --- a/ld/ldlang.c
> +++ b/ld/ldlang.c
> @@ -1345,6 +1345,12 @@ static lang_memory_region_type *lang_memory_region_list;
>  static lang_memory_region_type **lang_memory_region_list_tail
>    = &lang_memory_region_list;
>  
> +lang_memory_region_type *
> +get_memory_region_list ()
> +{
> +  return lang_memory_region_list;
> +}
> +
>  lang_memory_region_type *
>  lang_memory_region_lookup (const char *const name, bfd_boolean create)
>  {
> @@ -1798,7 +1804,7 @@ output_prev_sec_find (lang_output_section_statement_type *os)
>     insert non-alloc note sections among assignments setting end of
>     image symbols.  */
>  
> -static lang_statement_union_type **
> +lang_statement_union_type **
>  insert_os_after (lang_output_section_statement_type *after)
>  {
>    lang_statement_union_type **where;
> diff --git a/ld/ldlang.h b/ld/ldlang.h
> index 196debfa37..631e50d027 100644
> --- a/ld/ldlang.h
> +++ b/ld/ldlang.h
> @@ -520,6 +520,8 @@ extern struct asneeded_minfo **asneeded_list_tail;
>  
>  extern void (*output_bfd_hash_table_free_fn) (struct bfd_link_hash_table *);
>  
> +extern lang_memory_region_type * get_memory_region_list (void);
> +
>  extern void lang_init
>    (void);
>  extern void lang_finish
> @@ -595,6 +597,9 @@ extern void ldlang_add_file
>  extern lang_output_section_statement_type *lang_output_section_find_by_flags
>    (const asection *, flagword, lang_output_section_statement_type **,
>     lang_match_sec_type_func);
> +
> +extern lang_statement_union_type ** insert_os_after
> +(lang_output_section_statement_type *after);
>  extern lang_output_section_statement_type *lang_insert_orphan
>    (asection *, const char *, int, lang_output_section_statement_type *,
>     struct orphan_save *, etree_type *, lang_statement_list_type *);
> diff --git a/ld/testsuite/ld-arm/abind-1.d b/ld/testsuite/ld-arm/abind-1.d
> new file mode 100644
> index 0000000000..1b2ad44d56
> --- /dev/null
> +++ b/ld/testsuite/ld-arm/abind-1.d
> @@ -0,0 +1,49 @@
> +#name: SHF_GNU_ABIND 1 (using default linker script)
> +#source: abind-1.s
> +#ld: -e _start
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +00008000 t addr_0x8000_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +0000b002 r addr_0xb002
> +#...
> +0000b100 t addr_0xb100
> +#...
> +0000b200 r addr_0xb200_size_0x1000
> +#...
> +0000c402 d addr_0xc402
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0000e000 b addr_0xe000_size_0x1000
> +#...
> +0000f000 b addr_0xf000
> +#pass
> diff --git a/ld/testsuite/ld-arm/abind-1.s b/ld/testsuite/ld-arm/abind-1.s
> new file mode 100644
> index 0000000000..68183a3bbb
> --- /dev/null
> +++ b/ld/testsuite/ld-arm/abind-1.s
> @@ -0,0 +1,183 @@
> +/* abind-2*.ld linker scripts don't have rules for uniquely named sections.
> +   This means they will be orphaned which will better test the intermixing of
> +   .abind and regular sections, instead of just having one large indivisible
> +   block for each output section.  */
> +
> +/* Arbitrary sized .data sections. */
> +	.section	.data.size_0x1,"aw"
> +	.type	data_size_0x1, %object
> +data_size_0x1:
> +	.zero	0x1
> +
> +	.section	.data.size_0x10,"aw"
> +	.type	data_size_0x10, %object
> +data_size_0x10:
> +	.zero	0x10
> +
> +	.section	.data.size_0x80,"aw"
> +	.type	data_size_0x80, %object
> +data_size_0x80:
> +	.zero	0x80
> +
> +	.section	.data.size_0x100,"aw"
> +	.type	data_size_0x100, %object
> +data_size_0x100:
> +	.zero	0x100
> +
> +	.section	.data.size_0x800,"aw"
> +	.type	data_size_0x800, %object
> +data_size_0x800:
> +	.zero	0x800
> +
> +	.section	.data.size_0x1000,"aw"
> +	.type	data_size_0x1000, %object
> +data_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .bss sections.  */
> +
> +	.section	.bss.size_0x1,"aw",%nobits
> +	.type	bss_size_0x1, %object
> +bss_size_0x1:
> +	.zero	0x1
> +
> +	.section	.bss.size_0x10,"aw",%nobits
> +	.type	bss_size_0x10, %object
> +bss_size_0x10:
> +	.zero	0x10
> +
> +	.section	.bss.size_0x80,"aw",%nobits
> +	.type	bss_size_0x80, %object
> +bss_size_0x80:
> +	.zero	0x80
> +
> +	.section	.bss.size_0x100,"aw",%nobits
> +	.type	bss_size_0x100, %object
> +bss_size_0x100:
> +	.zero	0x100
> +
> +	.section	.bss.size_0x800,"aw",%nobits
> +	.type	bss_size_0x800, %object
> +bss_size_0x800:
> +	.zero	0x800
> +
> +	.section	.bss.size_0x1000,"aw",%nobits
> +	.type	bss_size_0x1000, %object
> +bss_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .text sections.  */
> +
> +	.section	.text.size_0x1,"ax",%progbits
> +	.type	text_size_0x1, %function
> +text_size_0x1:
> +	.zero	0x1
> +
> +	.section	.text.size_0x10,"ax",%progbits
> +	.type	text_size_0x10, %function
> +text_size_0x10:
> +	.zero	0x10
> +
> +	.section	.text.size_0x80,"ax",%progbits
> +	.type	text_size_0x80, %function
> +text_size_0x80:
> +	.zero	0x80
> +
> +	.section	.text.size_0x100,"ax",%progbits
> +	.type	text_size_0x100, %function
> +text_size_0x100:
> +	.zero	0x100
> +
> +	.section	.text.size_0x800,"ax",%progbits
> +	.type	text_size_0x800, %function
> +text_size_0x800:
> +	.zero	0x800
> +
> +	.section	.text.size_0x1000,"ax",%progbits
> +	.type	text_size_0x1000, %function
> +text_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .rodata sections.  */
> +
> +	.section	.rodata.size_0x1,"a"
> +	.type	rodata_size_0x1, %object
> +rodata_size_0x1:
> +	.zero	0x1
> +
> +	.section	.rodata.size_0x10,"a"
> +	.type	rodata_size_0x10, %object
> +rodata_size_0x10:
> +	.zero	0x10
> +
> +	.section	.rodata.size_0x80,"a"
> +	.type	rodata_size_0x80, %object
> +rodata_size_0x80:
> +	.zero	0x80
> +
> +	.section	.rodata.size_0x100,"a"
> +	.type	rodata_size_0x100, %object
> +rodata_size_0x100:
> +	.zero	0x100
> +
> +	.section	.rodata.size_0x800,"a"
> +	.type	rodata_size_0x800, %object
> +rodata_size_0x800:
> +	.zero	0x800
> +
> +	.section	.rodata.size_0x1000,"a"
> +	.type	rodata_size_0x1000, %object
> +rodata_size_0x1000:
> +	.zero	0x1000
> +
> +/* Start .gnu.abind sections.  */
> +
> +/* Fix a large section to the start of the region used for the LMA region of
> +   the .data sections.  This will expose any placement issues causing
> +   overlapping LMAs.  */
> +	.section	.text.addr_0x8000,"axA",%progbits,0x8000
> +	.type	addr_0x8000, %function
> +addr_0x8000_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.rodata.addr_0xb002,"aA",0xb002
> +	.type	addr_0xb002, %object
> +addr_0xb002:
> +	.byte	2
> +
> +	.section	.text.addr_0xb100,"axA",%progbits,0xb100
> +	.type	addr_0xb100, %function
> +addr_0xb100:
> +	.zero 2
> +
> +	.section	.rodata.addr_0xb200,"aA",0xb200
> +	.type	addr_0xb200, %object
> +addr_0xb200_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.data.addr_0xc402,"awA",0xc402
> +	.type	addr_0xc402, %object
> +addr_0xc402:
> +	.zero 2
> +
> +	.section	.bss.addr_0xe000,"awA",%nobits,0xe000
> +	.type	addr_0xe000, %object
> +addr_0xe000_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.bss.addr_0xf000,"awA",%nobits,0x0f000
> +	.type	addr_0xf000, %object
> +addr_0xf000:
> +	.zero	1
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> +
> +	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
> +	.global	__run_gnu_abind_init_array
> +	.type	__run_gnu_abind_init_array, %function
> +__run_gnu_abind_init_array:
> +	.word 0
> diff --git a/ld/testsuite/ld-arm/arm-elf.exp b/ld/testsuite/ld-arm/arm-elf.exp
> index 0b47d636df..11b06b28a6 100644
> --- a/ld/testsuite/ld-arm/arm-elf.exp
> +++ b/ld/testsuite/ld-arm/arm-elf.exp
> @@ -1269,6 +1269,8 @@ run_dump_test "non-contiguous-arm4"
>  run_dump_test "non-contiguous-arm5"
>  run_dump_test "non-contiguous-arm6"
>  
> +run_dump_test "abind-1"
> +
>  if { ![istarget "arm*-*-nacl*"] } {
>      run_dump_test "thumb-plt"
>      run_dump_test "thumb-plt-got"
> diff --git a/ld/testsuite/ld-elf/abind-1.s b/ld/testsuite/ld-elf/abind-1.s
> new file mode 100644
> index 0000000000..aa3f1fcf82
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1.s
> @@ -0,0 +1,57 @@
> +/* This should get placed before addr_0x8200, at 0x8000.  */
> +	.section	.data.size_0x100,"aw"
> +	.type	size_0x100, %object
> +size_0x100:
> +	.zero	0x100
> +
> +/* This should get placed after addr_0x8200.  */
> +	.section	.bss.size_0x150,"aw",%nobits
> +	.type	size_0x150, %object
> +size_0x150:
> +	.zero	0x150
> +
> +	.section	.data.addr_0x8200,"awA",0x8200
> +	.type	addr_0x8200, %object
> +addr_0x8200:
> +	.short	1
> +
> +	.section	.bss.addr_0x9000,"awA",%nobits,0x9000
> +	.type	addr_0x9000, %object
> +addr_0x9000:
> +	.zero	1
> +
> +/* This should be the last section in the region.  */
> +	.section	.bss.addr_0xa000,"awA",%nobits,0xa000
> +	.type	addr_0xa000, %object
> +addr_0xa000:
> +	.zero	1
> +
> +/* Fix a large section to the start of the region used for the LMA region of
> +   the .data sections.  This will expose any placement issues causing
> +   overlapping LMAs.  */
> +	.section	.text.addr_0x0,"axA",%progbits,0x0
> +	.type	addr_0x0, %function
> +addr_0x0:
> +	.zero 0x1000
> +
> +	.section	.text.size_0x500,"ax",%progbits
> +	.type	size_0x500, %function
> +size_0x500:
> +	.zero 0x1000
> +
> +	.section	.rodata.addr_0x1002,"aA",0x1002
> +	.type	addr_0x1002, %object
> +addr_0x1002:
> +	.byte	2
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> +
> +	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
> +	.global	__run_gnu_abind_init_array
> +	.type	__run_gnu_abind_init_array, %function
> +__run_gnu_abind_init_array:
> +	.word 0
> diff --git a/ld/testsuite/ld-elf/abind-1a.d b/ld/testsuite/ld-elf/abind-1a.d
> new file mode 100644
> index 0000000000..1b758fef59
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1a.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1a
> +#source: abind-1.s
> +#ld: -e _start -T abind-1a.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +0+8200 . addr_0x8200
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1a.ld b/ld/testsuite/ld-elf/abind-1a.ld
> new file mode 100644
> index 0000000000..dd3a18ef70
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1a.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > ROM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM AT> ROM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1b.d b/ld/testsuite/ld-elf/abind-1b.d
> new file mode 100644
> index 0000000000..41c926529a
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1b.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1b
> +#source: abind-1.s
> +#ld: -e _start -T abind-1b.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +0+8200 . addr_0x8200
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1b.ld b/ld/testsuite/ld-elf/abind-1b.ld
> new file mode 100644
> index 0000000000..99f9163b7f
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1b.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
> +}
> +
> +SECTIONS
> +{
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM AT> ROM
> +
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > ROM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1c.d b/ld/testsuite/ld-elf/abind-1c.d
> new file mode 100644
> index 0000000000..740e6b112d
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1c.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1c
> +#source: abind-1.s
> +#ld: -e _start -T abind-1c.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +0+8200 . addr_0x8200
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1c.ld b/ld/testsuite/ld-elf/abind-1c.ld
> new file mode 100644
> index 0000000000..1587ef028e
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1c.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > ROM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM AT> ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > ROM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1d.d b/ld/testsuite/ld-elf/abind-1d.d
> new file mode 100644
> index 0000000000..9c2e64dc4c
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1d.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1d
> +#source: abind-1.s
> +#ld: -e _start -T abind-1d.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +0+8200 . addr_0x8200
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1d.ld b/ld/testsuite/ld-elf/abind-1d.ld
> new file mode 100644
> index 0000000000..852e03e645
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1d.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x6fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x6fff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM AT> ROM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > ROM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1e.d b/ld/testsuite/ld-elf/abind-1e.d
> new file mode 100644
> index 0000000000..ac1a78654f
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1e.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1e (No LMA region)
> +#source: abind-1.s
> +#ld: -e _start -T abind-1e.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +0+8200 . addr_0x8200
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1e.ld b/ld/testsuite/ld-elf/abind-1e.ld
> new file mode 100644
> index 0000000000..58b3dc075a
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1e.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xefff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1f.d b/ld/testsuite/ld-elf/abind-1f.d
> new file mode 100644
> index 0000000000..2981a4e6d1
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1f.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1f (Placement in other region)
> +#source: abind-1.s
> +#ld: -e _start -T abind-1f.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +0+8200 . addr_0x8200
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1f.ld b/ld/testsuite/ld-elf/abind-1f.ld
> new file mode 100644
> index 0000000000..7a655e0262
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1f.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0x9fff
> +  VECT     		: ORIGIN = 0xa000,	LENGTH = 0x4
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1g.d b/ld/testsuite/ld-elf/abind-1g.d
> new file mode 100644
> index 0000000000..90807c4951
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1g.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1g (Placement outside region)
> +#source: abind-1.s
> +#ld: -e _start -T abind-1g.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +0+8200 . addr_0x8200
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1g.ld b/ld/testsuite/ld-elf/abind-1g.ld
> new file mode 100644
> index 0000000000..8749d74626
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1g.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0x9fff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1h.d b/ld/testsuite/ld-elf/abind-1h.d
> new file mode 100644
> index 0000000000..a17f5d47a0
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1h.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1h (No flags set on region)
> +#source: abind-1.s
> +#ld: -e _start -T abind-1h.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +0+8200 . addr_0x8200
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1h.ld b/ld/testsuite/ld-elf/abind-1h.ld
> new file mode 100644
> index 0000000000..8092bb40d6
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1h.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM 		: ORIGIN = 0x0000,	LENGTH = 0xafff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-1i.d b/ld/testsuite/ld-elf/abind-1i.d
> new file mode 100644
> index 0000000000..17696c3760
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1i.d
> @@ -0,0 +1,27 @@
> +#name: SHF_GNU_ABIND 1i (No memory regions)
> +#source: abind-1.s
> +#ld: -e _start -T abind-1i.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 . addr_0x0
> +#...
> +0+1002 . addr_0x1002
> +#...
> +[0-9a-f]+ . size_0x150
> +#...
> +[0-9a-f]+ . size_0x100
> +#...
> +[0-9a-f]+ . size_0x500
> +#...
> +[0-9a-f]+ . _start
> +#...
> +[0-9a-f]+ . __run_gnu_abind_init_array
> +#...
> +0+8200 . addr_0x8200
> +#...
> +0+9000 . addr_0x9000
> +#...
> +0+a000 . addr_0xa000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-1i.ld b/ld/testsuite/ld-elf/abind-1i.ld
> new file mode 100644
> index 0000000000..9b763c7387
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-1i.ld
> @@ -0,0 +1,22 @@
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss .bss.*)
> +  }
> +
> +  .data :
> +  {
> +    *(.data .data.*)
> +  }
> +
> +  .text :
> +  {
> +    *(.text .text.*)
> +  }
> +
> +  .rodata :
> +  {
> +    *(.rodata .rodata.*)
> +  }
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2.s b/ld/testsuite/ld-elf/abind-2.s
> new file mode 100644
> index 0000000000..75777e4817
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2.s
> @@ -0,0 +1,183 @@
> +/* abind-2*.ld linker scripts don't have rules for uniquely named sections.
> +   This means they will be orphaned which will better test the intermixing of
> +   .abind and regular sections, instead of just having one large indivisible
> +   block for each output section.  */
> +
> +/* Arbitrary sized .data sections. */
> +	.section	.data.size_0x1,"aw"
> +	.type	data_size_0x1, %object
> +data_size_0x1:
> +	.zero	0x1
> +
> +	.section	.data.size_0x10,"aw"
> +	.type	data_size_0x10, %object
> +data_size_0x10:
> +	.zero	0x10
> +
> +	.section	.data.size_0x80,"aw"
> +	.type	data_size_0x80, %object
> +data_size_0x80:
> +	.zero	0x80
> +
> +	.section	.data.size_0x100,"aw"
> +	.type	data_size_0x100, %object
> +data_size_0x100:
> +	.zero	0x100
> +
> +	.section	.data.size_0x800,"aw"
> +	.type	data_size_0x800, %object
> +data_size_0x800:
> +	.zero	0x800
> +
> +	.section	.data.size_0x1000,"aw"
> +	.type	data_size_0x1000, %object
> +data_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .bss sections.  */
> +
> +	.section	.bss.size_0x1,"aw",%nobits
> +	.type	bss_size_0x1, %object
> +bss_size_0x1:
> +	.zero	0x1
> +
> +	.section	.bss.size_0x10,"aw",%nobits
> +	.type	bss_size_0x10, %object
> +bss_size_0x10:
> +	.zero	0x10
> +
> +	.section	.bss.size_0x80,"aw",%nobits
> +	.type	bss_size_0x80, %object
> +bss_size_0x80:
> +	.zero	0x80
> +
> +	.section	.bss.size_0x100,"aw",%nobits
> +	.type	bss_size_0x100, %object
> +bss_size_0x100:
> +	.zero	0x100
> +
> +	.section	.bss.size_0x800,"aw",%nobits
> +	.type	bss_size_0x800, %object
> +bss_size_0x800:
> +	.zero	0x800
> +
> +	.section	.bss.size_0x1000,"aw",%nobits
> +	.type	bss_size_0x1000, %object
> +bss_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .text sections.  */
> +
> +	.section	.text.size_0x1,"ax",%progbits
> +	.type	text_size_0x1, %function
> +text_size_0x1:
> +	.zero	0x1
> +
> +	.section	.text.size_0x10,"ax",%progbits
> +	.type	text_size_0x10, %function
> +text_size_0x10:
> +	.zero	0x10
> +
> +	.section	.text.size_0x80,"ax",%progbits
> +	.type	text_size_0x80, %function
> +text_size_0x80:
> +	.zero	0x80
> +
> +	.section	.text.size_0x100,"ax",%progbits
> +	.type	text_size_0x100, %function
> +text_size_0x100:
> +	.zero	0x100
> +
> +	.section	.text.size_0x800,"ax",%progbits
> +	.type	text_size_0x800, %function
> +text_size_0x800:
> +	.zero	0x800
> +
> +	.section	.text.size_0x1000,"ax",%progbits
> +	.type	text_size_0x1000, %function
> +text_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .rodata sections.  */
> +
> +	.section	.rodata.size_0x1,"a"
> +	.type	rodata_size_0x1, %object
> +rodata_size_0x1:
> +	.zero	0x1
> +
> +	.section	.rodata.size_0x10,"a"
> +	.type	rodata_size_0x10, %object
> +rodata_size_0x10:
> +	.zero	0x10
> +
> +	.section	.rodata.size_0x80,"a"
> +	.type	rodata_size_0x80, %object
> +rodata_size_0x80:
> +	.zero	0x80
> +
> +	.section	.rodata.size_0x100,"a"
> +	.type	rodata_size_0x100, %object
> +rodata_size_0x100:
> +	.zero	0x100
> +
> +	.section	.rodata.size_0x800,"a"
> +	.type	rodata_size_0x800, %object
> +rodata_size_0x800:
> +	.zero	0x800
> +
> +	.section	.rodata.size_0x1000,"a"
> +	.type	rodata_size_0x1000, %object
> +rodata_size_0x1000:
> +	.zero	0x1000
> +
> +/* Start .gnu.abind sections.  */
> +
> +/* Fix a large section to the start of the region used for the LMA region of
> +   the .data sections.  This will expose any placement issues causing
> +   overlapping LMAs.  */
> +	.section	.text.addr_0x0,"axA",%progbits,0x0
> +	.type	addr_0x0, %function
> +addr_0x0_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.rodata.addr_0x1002,"aA",0x1002
> +	.type	addr_0x1002, %object
> +addr_0x1002:
> +	.byte	2
> +
> +	.section	.text.addr_0x2000,"axA",%progbits,0x2000
> +	.type	addr_0x2000, %function
> +addr_0x2000:
> +	.zero 2
> +
> +	.section	.rodata.addr_0x3002,"aA",0x3002
> +	.type	addr_0x3002, %object
> +addr_0x3002_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.data.addr_0x8000,"awA",0x8000
> +	.type	addr_0x8000, %object
> +addr_0x8000:
> +	.zero 2
> +
> +	.section	.bss.addr_0x9000,"awA",%nobits,0x9000
> +	.type	addr_0x9000, %object
> +addr_0x9000_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.bss.addr_0xd000,"awA",%nobits,0xd000
> +	.type	addr_0xd000, %object
> +addr_0xd000:
> +	.zero	1
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> +
> +	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
> +	.global	__run_gnu_abind_init_array
> +	.type	__run_gnu_abind_init_array, %function
> +__run_gnu_abind_init_array:
> +	.word 0
> diff --git a/ld/testsuite/ld-elf/abind-2a.d b/ld/testsuite/ld-elf/abind-2a.d
> new file mode 100644
> index 0000000000..ec247ac951
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2a.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2a
> +#source: abind-2.s
> +#ld: -e _start -T abind-2a.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+8000 d addr_0x8000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2a.ld b/ld/testsuite/ld-elf/abind-2a.ld
> new file mode 100644
> index 0000000000..517366f395
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2a.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
> +}
> +
> +SECTIONS
> +{
> +  .text :
> +  {
> +    *(.text)
> +  } > ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > ROM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM AT> ROM
> +
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2b.d b/ld/testsuite/ld-elf/abind-2b.d
> new file mode 100644
> index 0000000000..66057ab3bd
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2b.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2b
> +#source: abind-2.s
> +#ld: -e _start -T abind-2b.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+8000 d addr_0x8000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2b.ld b/ld/testsuite/ld-elf/abind-2b.ld
> new file mode 100644
> index 0000000000..e180fabedb
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2b.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  ROM (rx)		: ORIGIN = 0x0000,  LENGTH = 0x7fff
> +  RAM (rwx)		: ORIGIN = 0x8000,	LENGTH = 0x7fff
> +}
> +
> +SECTIONS
> +{
> +  .text :
> +  {
> +    *(.text)
> +  } > ROM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > ROM
> +
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM AT> ROM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2c.d b/ld/testsuite/ld-elf/abind-2c.d
> new file mode 100644
> index 0000000000..74f62a36f1
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2c.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2c
> +#source: abind-2.s
> +#ld: -e _start -T abind-2c.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +0+8000 d addr_0x8000
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2c.ld b/ld/testsuite/ld-elf/abind-2c.ld
> new file mode 100644
> index 0000000000..3c553f4b31
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2c.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xefff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2d.d b/ld/testsuite/ld-elf/abind-2d.d
> new file mode 100644
> index 0000000000..1fb480843f
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2d.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2d
> +#source: abind-2.s
> +#ld: -e _start -T abind-2d.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +0+8000 d addr_0x8000
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2d.ld b/ld/testsuite/ld-elf/abind-2d.ld
> new file mode 100644
> index 0000000000..00b375bfbe
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2d.ld
> @@ -0,0 +1,28 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xcfff
> +  VECT     		: ORIGIN = 0xd000,	LENGTH = 0x4
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2e.d b/ld/testsuite/ld-elf/abind-2e.d
> new file mode 100644
> index 0000000000..ff15f34de8
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2e.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2e
> +#source: abind-2.s
> +#ld: -e _start -T abind-2e.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +0+8000 d addr_0x8000
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2e.ld b/ld/testsuite/ld-elf/abind-2e.ld
> new file mode 100644
> index 0000000000..b1bb54b3a4
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2e.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM (rwx)		: ORIGIN = 0x0000,	LENGTH = 0xcfff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2f.d b/ld/testsuite/ld-elf/abind-2f.d
> new file mode 100644
> index 0000000000..8a59b5da79
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2f.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2f
> +#source: abind-2.s
> +#ld: -e _start -T abind-2f.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +0+8000 d addr_0x8000
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2f.ld b/ld/testsuite/ld-elf/abind-2f.ld
> new file mode 100644
> index 0000000000..4867878538
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2f.ld
> @@ -0,0 +1,27 @@
> +MEMORY
> +{
> +  RAM 		: ORIGIN = 0x0000,	LENGTH = 0xdfff
> +}
> +
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss)
> +  } > RAM
> +
> +  .data :
> +  {
> +    *(.data)
> +  } > RAM
> +
> +  .text :
> +  {
> +    *(.text)
> +  } > RAM
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  } > RAM
> +}
> diff --git a/ld/testsuite/ld-elf/abind-2g.d b/ld/testsuite/ld-elf/abind-2g.d
> new file mode 100644
> index 0000000000..7084f5fcee
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2g.d
> @@ -0,0 +1,73 @@
> +#name: SHF_GNU_ABIND 2g
> +#source: abind-2.s
> +#ld: -e _start -T abind-2g.ld
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+0000 t addr_0x0_size_0x1000
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +#...
> +0+1002 r addr_0x1002
> +#...
> +[0-9a-f]+ . bss_size_0x10
> +#...
> +[0-9a-f]+ . bss_size_0x80
> +#...
> +[0-9a-f]+ . bss_size_0x100
> +#...
> +[0-9a-f]+ . bss_size_0x800
> +#...
> +0+2000 t addr_0x2000
> +#...
> +[0-9a-f]+ . bss_size_0x1000
> +#...
> +0+3002 r addr_0x3002_size_0x1000
> +#...
> +[0-9a-f]+ . data_size_0x1
> +#...
> +[0-9a-f]+ . data_size_0x10
> +#...
> +[0-9a-f]+ . data_size_0x80
> +#...
> +[0-9a-f]+ . data_size_0x100
> +#...
> +[0-9a-f]+ . data_size_0x800
> +#...
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +#...
> +[0-9a-f]+ . text_size_0x10
> +#...
> +[0-9a-f]+ . text_size_0x80
> +#...
> +[0-9a-f]+ . text_size_0x100
> +#...
> +[0-9a-f]+ . text_size_0x800
> +#...
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +#...
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +#...
> +[0-9a-f]+ . rodata_size_0x10
> +#...
> +[0-9a-f]+ . rodata_size_0x80
> +#...
> +[0-9a-f]+ . rodata_size_0x100
> +#...
> +[0-9a-f]+ . rodata_size_0x800
> +#...
> +0+8000 d addr_0x8000
> +#...
> +0+9000 b addr_0x9000_size_0x1000
> +#...
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+d000 b addr_0xd000
> +#pass
> diff --git a/ld/testsuite/ld-elf/abind-2g.ld b/ld/testsuite/ld-elf/abind-2g.ld
> new file mode 100644
> index 0000000000..2a9be2b5c8
> --- /dev/null
> +++ b/ld/testsuite/ld-elf/abind-2g.ld
> @@ -0,0 +1,22 @@
> +SECTIONS
> +{
> +  .bss :
> +  {
> +    *(.bss)
> +  }
> +
> +  .data :
> +  {
> +    *(.data)
> +  }
> +
> +  .text :
> +  {
> +    *(.text)
> +  }
> +
> +  .rodata :
> +  {
> +    *(.rodata)
> +  }
> +}
> diff --git a/ld/testsuite/ld-msp430-elf/location-1.d b/ld/testsuite/ld-msp430-elf/location-1.d
> new file mode 100644
> index 0000000000..9a23431033
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-1.d
> @@ -0,0 +1,23 @@
> +# Test that text, rodata, data and bss locsyms get placed at the correct
> +# addresses.
> +# The variable "either" should be placed in between the .data sections.
> +# 
> +#name: SMK_LOCATION data meta-information 
> +#ld: -e _start
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +00000510 . loc510_data
> +00000520 . loc520_data
> +000005f1 . loc5f1_data
> +#...
> +[0-9a-f]+ . either
> +#...
> +00000790 . loc790_data
> +00000808 . loc808_data
> +00000850 . loc850_bss
> +#...
> +00009000 . loc9000_text
> +0000910a . loc910a_rodata
> +#pass
> diff --git a/ld/testsuite/ld-msp430-elf/location-1.s b/ld/testsuite/ld-msp430-elf/location-1.s
> new file mode 100644
> index 0000000000..83d179d281
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-1.s
> @@ -0,0 +1,98 @@
> +	.global	arr_1
> +	.section	.data.arr_1,"aw"
> +	.type	arr_1, %object
> +arr_1:
> +	.zero	80
> +
> +	.global	arr_2
> +	.section	.data.arr_2,"aw"
> +	.type	arr_2, %object
> +arr_2:
> +	.zero	80
> +
> +	.global	arr_3
> +	.section	.data.arr_3,"aw"
> +	.type	arr_3, %object
> +arr_3:
> +	.zero	80
> +
> +	.global	arr_4
> +	.section	.data.arr_4,"aw"
> +	.type	arr_4, %object
> +arr_4:
> +	.zero	80
> +
> +/*
> +	.global	const_arr_1
> +	.section	.rodata.const_arr_1,"a"
> +	.type	const_arr_1, %object
> +const_arr_1:
> +	.zero	80
> +  */
> +
> +
> +	.global	loc808_data
> +	.section	.data.loc808_data,"awA",0x808
> +	.type	loc808_data, %object
> +loc808_data:
> +	.short	2056
> +
> +	.global	loc510_data
> +	.section	.data.loc510_data,"awA",0x510
> +	.type	loc510_data, %object
> +loc510_data:
> +	.byte	81
> +
> +	.global	loc520_data
> +	.section	.data.loc520_data,"awA",0x520
> +	.type	loc520_data, %object
> +loc520_data:
> +	.byte	82
> +
> +	.global	loc790_data
> +	.section	.data.loc790_data,"awA",0x790
> +	.type	loc790_data, %object
> +loc790_data:
> +	.byte	114
> +
> +	.global	loc5f1_data
> +	.section	.data.loc5f1_data,"awA",0x5f1
> +	.type	loc5f1_data, %object
> +loc5f1_data:
> +	.byte	95
> +
> +	.section	.text.loc9000_text,"axA",%progbits,0x9000
> +	.global	loc9000_text
> +	.type	loc9000_text, %function
> +loc9000_text:
> +	.word 0
> +
> +	.global	loc910a_rodata
> +	.section	.rodata.loc910a_rodata,"aA",0x910a
> +	.type	loc910a_rodata, %object
> +loc910a_rodata:
> +	.byte	2
> +
> +	.global	loc850_bss
> +	.section	.bss.loc850_bss,"awA",%nobits,0x850
> +	.type	loc850_bss, %object
> +loc850_bss:
> +	.zero	1
> +
> +	.global	normal_bss
> +	.section	.bss.normal_bss,"aw",%nobits
> +	.type	normal_bss, %object
> +normal_bss:
> +	.zero	1
> +
> +	.global	either
> +	.section	.either.data,"aw"
> +	.type	either, %object
> +either:
> +	.byte	1
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> diff --git a/ld/testsuite/ld-msp430-elf/location-2.d b/ld/testsuite/ld-msp430-elf/location-2.d
> new file mode 100644
> index 0000000000..87f3d4555e
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-2.d
> @@ -0,0 +1,30 @@
> +# Test that relaxation of branch instructions to jumps does not interfere
> +# with accurrate placement of sections.
> +# Before location placement, the BR instructions are all close enough to their
> +# destination such that they can be relaxed to JMP.  However, after placement
> +# of the sections, some of the JMP will now be out of range, so check that they
> +# get relaxed back to a BR (tested by the fact there are no relocation
> +# overflows), and that the functions still get placed at the
> +# correct address.
> +#name: SMK_LOCATION meta-information placement after relaxation
> +#ld: -e _start
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+8000 . fn_8000
> +#...
> +0+8200 . fn_8200
> +#...
> +0+8500 . fn_dst
> +#...
> +0+8600 . fn_8600
> +#...
> +0+8700 . fn_8700
> +#...
> +0+8a00 . fn_8a00
> +#...
> +0+9000 . fn_9000
> +#...
> +0+910a . loc910a_rodata
> +#pass
> diff --git a/ld/testsuite/ld-msp430-elf/location-2.s b/ld/testsuite/ld-msp430-elf/location-2.s
> new file mode 100644
> index 0000000000..0c278a1f78
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-2.s
> @@ -0,0 +1,76 @@
> +	.global	loc910a_rodata
> +	.section	.rodata.loc910a_rodata,"a"
> +	.type	loc910a_rodata, @object
> +loc910a_rodata:
> +	.byte	2
> +	.location 0x910a
> +
> +	.section	.text.fn_8700,"ax",@progbits
> +	.global	fn_8700
> +	.type	fn_8700, @function
> +fn_8700:
> +	br #fn_dst
> +	br #fn_9000
> +	NOP
> +	RET
> +	.section	.text.fn_8a00,"ax",@progbits
> +	.global	fn_8a00
> +	.type	fn_8a00, @function
> +fn_8a00:
> +	br #fn_dst
> +	br #fn_9000
> +	NOP
> +	RET
> +	.section	.text.fn_8600,"ax",@progbits
> +	.global	fn_8600
> +	.type	fn_8600, @function
> +fn_8600:
> +	br #fn_dst
> +	br #fn_9000
> +	NOP
> +	RET
> +	.section	.text.fn_8000,"ax",@progbits
> +	.global	fn_8000
> +	.type	fn_8000, @function
> +fn_8000:
> +	br #fn_dst
> +	br #fn_9000
> +	NOP
> +	RET
> +	.section	.text.fn_8200,"ax",@progbits
> +	.global	fn_8200
> +	.type	fn_8200, @function
> +fn_8200:
> +	br #fn_dst
> +	br #fn_9000
> +	NOP
> +	RET
> +  .section	.text.fn_9000,"ax",@progbits
> +	.global	fn_9000
> +	.type	fn_9000, @function
> +fn_9000:
> +	br #fn_dst
> +	NOP
> +	RET
> +	.section	.text.fn_dst,"ax",@progbits
> +	.global	fn_dst
> +	.type	fn_dst, @function
> +fn_dst:
> +	add r12, r12
> +	NOP
> +	RET
> +	.location  0x8000, .text.fn_8000
> +	.location 0x8500, .text.fn_dst
> +	.location  0x8200, .text.fn_8200
> +	.location  0x9000, .text.fn_9000
> +	.location  0x8600, .text.fn_8600
> +	.location  0x8700, .text.fn_8700
> +	.location  0x8a00, .text.fn_8a00
> +	.section	.text._start,"ax",@progbits
> +	.global	_start
> +	.type	_start, @function
> +_start:
> +	CALL	#fn_8000
> +	MOV.B	#0, R12
> +	.refsym	__crt0_call_exit
> +	RET
> diff --git a/ld/testsuite/ld-msp430-elf/location-3.d b/ld/testsuite/ld-msp430-elf/location-3.d
> new file mode 100644
> index 0000000000..0a2becf6fc
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-3.d
> @@ -0,0 +1,10 @@
> +# Test that a text locsym in a random section still gets placed.
> +# 
> +#name: Orphaned location section
> +#ld: -e _start
> +#notarget: ![supports_gnu_osabi]
> +#nm : -n
> +
> +#...
> +00009100 . loc9100_text
> +#pass
> diff --git a/ld/testsuite/ld-msp430-elf/location-3.s b/ld/testsuite/ld-msp430-elf/location-3.s
> new file mode 100644
> index 0000000000..2a2e970933
> --- /dev/null
> +++ b/ld/testsuite/ld-msp430-elf/location-3.s
> @@ -0,0 +1,12 @@
> +	.section	.faketext,"ax",%progbits
> +	.global	loc9100_text
> +	.type	loc9100_text, %function
> +loc9100_text:
> +	.word 0
> +	.location 0x9100
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> diff --git a/ld/testsuite/ld-msp430-elf/msp430-elf.exp b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
> index 875b413c14..22d476a4a5 100644
> --- a/ld/testsuite/ld-msp430-elf/msp430-elf.exp
> +++ b/ld/testsuite/ld-msp430-elf/msp430-elf.exp
> @@ -190,6 +190,10 @@ run_ld_link_tests $msp430eithershuffletests
>  run_ld_link_tests $msp430warntests
>  
>  run_dump_test valid-map
> +run_dump_test location-1
> +run_dump_test location-2
> +run_dump_test location-3
> +
>  run_ld_link_tests {{ "Check no reloc overflow with #lo and data in the upper region"
>          "-m msp430X" "" "" {reloc-lo-430x.s} {} "reloc-lo-430x"}}
>  run_ld_link_tests {{ "Check .upper prefixed input sections can be placed"
> diff --git a/ld/testsuite/ld-x86-64/abind-1.d b/ld/testsuite/ld-x86-64/abind-1.d
> new file mode 100644
> index 0000000000..33e91e7c51
> --- /dev/null
> +++ b/ld/testsuite/ld-x86-64/abind-1.d
> @@ -0,0 +1,54 @@
> +#name: SHF_GNU_ABIND 1 (using default linker script)
> +#source: abind-1.s
> +#ld: -e _start
> +#notarget: ![supports_gnu_osabi]
> +#nm: -n
> +
> +#...
> +0+400000 t addr_0x400000_size_0x1000
> +#...
> +[0-9a-f]+ . text_size_0x1
> +[0-9a-f]+ . text_size_0x10
> +[0-9a-f]+ . text_size_0x80
> +[0-9a-f]+ . text_size_0x100
> +[0-9a-f]+ . text_size_0x800
> +[0-9a-f]+ . text_size_0x1000
> +#...
> +[0-9a-f]+ T _start
> +[0-9a-f]+ T __run_gnu_abind_init_array
> +#...
> +0+404002 r addr_0x404002
> +0+406100 t addr_0x406100
> +#...
> +[0-9a-f]+ . rodata_size_0x1
> +[0-9a-f]+ . rodata_size_0x10
> +[0-9a-f]+ . rodata_size_0x80
> +[0-9a-f]+ . rodata_size_0x100
> +[0-9a-f]+ . rodata_size_0x800
> +[0-9a-f]+ . rodata_size_0x1000
> +#...
> +0+40b200 r addr_0x40b200_size_0x1000
> +#...
> +0+40c402 d addr_0x40c402
> +#...
> +[0-9a-f]+ . data_size_0x1
> +[0-9a-f]+ . data_size_0x10
> +[0-9a-f]+ . data_size_0x80
> +[0-9a-f]+ . data_size_0x100
> +[0-9a-f]+ . data_size_0x800
> +[0-9a-f]+ . data_size_0x1000
> +#...
> +0+40e000 b addr_0x40e000_size_0x1000
> +#...
> +0+40f000 b addr_0x40f000
> +#...
> +[0-9a-f]+ . __bss_start
> +[0-9a-f]+ . _edata
> +#...
> +[0-9a-f]+ . bss_size_0x1
> +[0-9a-f]+ . bss_size_0x10
> +[0-9a-f]+ . bss_size_0x80
> +[0-9a-f]+ . bss_size_0x100
> +[0-9a-f]+ . bss_size_0x800
> +[0-9a-f]+ . bss_size_0x1000
> +#pass
> diff --git a/ld/testsuite/ld-x86-64/abind-1.s b/ld/testsuite/ld-x86-64/abind-1.s
> new file mode 100644
> index 0000000000..c7068c7581
> --- /dev/null
> +++ b/ld/testsuite/ld-x86-64/abind-1.s
> @@ -0,0 +1,175 @@
> +/* Arbitrary sized .data sections. */
> +	.section	.data.size_0x1,"aw"
> +	.type	data_size_0x1, %object
> +data_size_0x1:
> +	.zero	0x1
> +
> +	.section	.data.size_0x10,"aw"
> +	.type	data_size_0x10, %object
> +data_size_0x10:
> +	.zero	0x10
> +
> +	.section	.data.size_0x80,"aw"
> +	.type	data_size_0x80, %object
> +data_size_0x80:
> +	.zero	0x80
> +
> +	.section	.data.size_0x100,"aw"
> +	.type	data_size_0x100, %object
> +data_size_0x100:
> +	.zero	0x100
> +
> +	.section	.data.size_0x800,"aw"
> +	.type	data_size_0x800, %object
> +data_size_0x800:
> +	.zero	0x800
> +
> +	.section	.data.size_0x1000,"aw"
> +	.type	data_size_0x1000, %object
> +data_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .bss sections.  */
> +
> +	.section	.bss.size_0x1,"aw",%nobits
> +	.type	bss_size_0x1, %object
> +bss_size_0x1:
> +	.zero	0x1
> +
> +	.section	.bss.size_0x10,"aw",%nobits
> +	.type	bss_size_0x10, %object
> +bss_size_0x10:
> +	.zero	0x10
> +
> +	.section	.bss.size_0x80,"aw",%nobits
> +	.type	bss_size_0x80, %object
> +bss_size_0x80:
> +	.zero	0x80
> +
> +	.section	.bss.size_0x100,"aw",%nobits
> +	.type	bss_size_0x100, %object
> +bss_size_0x100:
> +	.zero	0x100
> +
> +	.section	.bss.size_0x800,"aw",%nobits
> +	.type	bss_size_0x800, %object
> +bss_size_0x800:
> +	.zero	0x800
> +
> +	.section	.bss.size_0x1000,"aw",%nobits
> +	.type	bss_size_0x1000, %object
> +bss_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .text sections.  */
> +
> +	.section	.text.size_0x1,"ax",%progbits
> +	.type	text_size_0x1, %function
> +text_size_0x1:
> +	.zero	0x1
> +
> +	.section	.text.size_0x10,"ax",%progbits
> +	.type	text_size_0x10, %function
> +text_size_0x10:
> +	.zero	0x10
> +
> +	.section	.text.size_0x80,"ax",%progbits
> +	.type	text_size_0x80, %function
> +text_size_0x80:
> +	.zero	0x80
> +
> +	.section	.text.size_0x100,"ax",%progbits
> +	.type	text_size_0x100, %function
> +text_size_0x100:
> +	.zero	0x100
> +
> +	.section	.text.size_0x800,"ax",%progbits
> +	.type	text_size_0x800, %function
> +text_size_0x800:
> +	.zero	0x800
> +
> +	.section	.text.size_0x1000,"ax",%progbits
> +	.type	text_size_0x1000, %function
> +text_size_0x1000:
> +	.zero	0x1000
> +
> +/* Arbitrary sized .rodata sections.  */
> +
> +	.section	.rodata.size_0x1,"a"
> +	.type	rodata_size_0x1, %object
> +rodata_size_0x1:
> +	.zero	0x1
> +
> +	.section	.rodata.size_0x10,"a"
> +	.type	rodata_size_0x10, %object
> +rodata_size_0x10:
> +	.zero	0x10
> +
> +	.section	.rodata.size_0x80,"a"
> +	.type	rodata_size_0x80, %object
> +rodata_size_0x80:
> +	.zero	0x80
> +
> +	.section	.rodata.size_0x100,"a"
> +	.type	rodata_size_0x100, %object
> +rodata_size_0x100:
> +	.zero	0x100
> +
> +	.section	.rodata.size_0x800,"a"
> +	.type	rodata_size_0x800, %object
> +rodata_size_0x800:
> +	.zero	0x800
> +
> +	.section	.rodata.size_0x1000,"a"
> +	.type	rodata_size_0x1000, %object
> +rodata_size_0x1000:
> +	.zero	0x1000
> +
> +/* Start .gnu.abind sections.  */
> +
> +	.section	.text.addr_0x400000,"axA",%progbits,0x400000
> +	.type	addr_0x400000, %function
> +addr_0x400000_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.rodata.addr_0x404002,"aA",0x404002
> +	.type	addr_0x404002, %object
> +addr_0x404002:
> +	.byte	2
> +
> +	.section	.text.addr_0x406100,"axA",%progbits,0x406100
> +	.type	addr_0x406100, %function
> +addr_0x406100:
> +	.zero 2
> +
> +	.section	.rodata.addr_0x40b200,"aA",0x40b200
> +	.type	addr_0x40b200, %object
> +addr_0x40b200_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.data.addr_0x40c402,"awA",0x40c402
> +	.type	addr_0x40c402, %object
> +addr_0x40c402:
> +	.zero 2
> +
> +	.section	.bss.addr_0x40e000,"awA",%nobits,0x40e000
> +	.type	addr_0x40e000, %object
> +addr_0x40e000_size_0x1000:
> +	.zero 0x1000
> +
> +	.section	.bss.addr_0x40f000,"awA",%nobits,0x40f000
> +	.type	addr_0x40f000, %object
> +addr_0x40f000:
> +	.zero	1
> +
> +	.section	.text._start,"ax",%progbits
> +	.global	_start
> +	.type	_start, %function
> +_start:
> +	.word 0
> +
> +	.section	.text.__run_gnu_abind_init_array,"ax",%progbits
> +	.global	__run_gnu_abind_init_array
> +	.type	__run_gnu_abind_init_array, %function
> +__run_gnu_abind_init_array:
> +	.word 0
> diff --git a/ld/testsuite/ld-x86-64/x86-64.exp b/ld/testsuite/ld-x86-64/x86-64.exp
> index 59cad54a79..5f61135ccb 100644
> --- a/ld/testsuite/ld-x86-64/x86-64.exp
> +++ b/ld/testsuite/ld-x86-64/x86-64.exp
> @@ -245,6 +245,7 @@ if { ![ld_link $ld tmpdir/$test "-m$emul tmpdir/${test}a.o tmpdir/${test}b.o"] }
>      }
>  }
>  
> +run_dump_test "abind-1"
>  run_dump_test "abs"
>  run_dump_test "abs-k1om"
>  run_dump_test "abs-l1om"
> -- 
> 2.28.0
> 


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-10-23 10:20 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-20 10:05 [RFC] SHF_GNU_ABIND Section Flag Jozef Lawrynowicz
2020-10-23 10:20 ` Jozef Lawrynowicz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).