Perform on-demand global register allocation from
authorHans-Peter Nilsson <hp@axis.com>
Fri, 1 Feb 2002 08:18:56 +0000 (08:18 +0000)
committerHans-Peter Nilsson <hp@axis.com>
Fri, 1 Feb 2002 08:18:56 +0000 (08:18 +0000)
R_MMIX_BASE_PLUS_OFFSET relocs.
* elf64-mmix.c (struct bpo_reloc_section_info, struct
bpo_reloc_request, struct bpo_greg_section_info): New.
(mmix_elf_check_common_relocs, mmix_elf_gc_sweep_hook,
bpo_reloc_request_sort_fn, mmix_elf_relax_section,
_bfd_mmix_check_all_relocs,
_bfd_mmix_prepare_linker_allocated_gregs,
_bfd_mmix_finalize_linker_allocated_gregs): New functions.
(elf_mmix_howto_table): Correct src_mask for most relocs.
(mmix_elf_perform_relocation) <case R_MMIX_BASE_PLUS_OFFSET>: New
case.
(mmix_final_link_relocate) <case R_MMIX_BASE_PLUS_OFFSET>: New
case.  Fix typo in comment.  New label do_mmix_reloc.
(mmix_elf_check_relocs): Abuse bfd_link_info member base_file to
store first object file with a base-plus-offset reloc.  Call
mmix_elf_check_common_relocs for the part common with mmo.
(mmix_elf_final_link): Write out linker-allocated register
contents section.
(elf_backend_gc_sweep_hook): Define.
(bfd_elf64_bfd_relax_section): Define.

* mmo.c: Don't include <ctype.h>
(mmo_init): Correct init-once logic.

bfd/ChangeLog
bfd/elf64-mmix.c
bfd/mmo.c

index 3df653f45cff5937c6f3afb8127bbbcc3362692d..bbcc9e708cc1bdfbfb4810847b997a4b88b9e417 100644 (file)
@@ -1,3 +1,30 @@
+2002-02-01  Hans-Peter Nilsson  <hp@bitrange.com>
+
+       Perform on-demand global register allocation from
+       R_MMIX_BASE_PLUS_OFFSET relocs.
+       * elf64-mmix.c (struct bpo_reloc_section_info, struct
+       bpo_reloc_request, struct bpo_greg_section_info): New.
+       (mmix_elf_check_common_relocs, mmix_elf_gc_sweep_hook,
+       bpo_reloc_request_sort_fn, mmix_elf_relax_section,
+       _bfd_mmix_check_all_relocs,
+       _bfd_mmix_prepare_linker_allocated_gregs,
+       _bfd_mmix_finalize_linker_allocated_gregs): New functions.
+       (elf_mmix_howto_table): Correct src_mask for most relocs.
+       (mmix_elf_perform_relocation) <case R_MMIX_BASE_PLUS_OFFSET>: New
+       case.
+       (mmix_final_link_relocate) <case R_MMIX_BASE_PLUS_OFFSET>: New
+       case.  Fix typo in comment.  New label do_mmix_reloc.
+       (mmix_elf_check_relocs): Abuse bfd_link_info member base_file to
+       store first object file with a base-plus-offset reloc.  Call
+       mmix_elf_check_common_relocs for the part common with mmo.
+       (mmix_elf_final_link): Write out linker-allocated register
+       contents section.
+       (elf_backend_gc_sweep_hook): Define.
+       (bfd_elf64_bfd_relax_section): Define.
+
+       * mmo.c: Don't include <ctype.h>
+       (mmo_init): Correct init-once logic.
+
 2002-02-01  Tom Rix  <trix@redhat.com>
 
        * config.bfd: Conditionally support <aiaff> for pre AIX 4.3.
index a1148b09acd176848e31d7fd617b3cf3688d8108..9da71450789a66039f01d074a8fec4d3d7f58ab5 100644 (file)
@@ -21,8 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 /* No specific ABI or "processor-specific supplement" defined.  */
 
 /* TODO:
-   - Linker relaxation.
-   - On-demand register allocation (from R_MMIX_BASE_PLUS_OFFSET).  */
+   - Linker relaxation.  */
 
 #include "bfd.h"
 #include "sysdep.h"
@@ -42,6 +41,85 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  _bfd_abort (__FILE__, __LINE__,               \
             "bad case for " #x)
 
+/* For each section containing a base-plus-offset (BPO) reloc, we attach
+   this struct as elf_section_data (section)->tdata, which is otherwise
+   NULL.  */
+struct bpo_reloc_section_info
+  {
+    /* The base is 1; this is the first number in this section.  */
+    size_t first_base_plus_offset_reloc;
+
+    /* Number of BPO-relocs in this section.  */
+    size_t n_bpo_relocs_this_section;
+
+    /* Running index, used at relocation time.  */
+    size_t bpo_index;
+
+    /* We don't have access to the bfd_link_info struct in
+       mmix_final_link_relocate.  What we really want to get at is the
+       global single struct greg_relocation, so we stash it here.  */
+    asection *bpo_greg_section;
+  };
+
+/* Helper struct (in global context) for the one below.
+   There's one of these created for every BPO reloc.  */
+struct bpo_reloc_request
+  {
+    bfd_vma value;
+
+    /* Valid after relaxation.  The base is 0; the first register number
+       must be added.  The offset is in range 0..255.  */
+    size_t regindex;
+    size_t offset;
+
+    /* The order number for this BPO reloc, corresponding to the order in
+       which BPO relocs were found.  Used to create an index after reloc
+       requests are sorted.  */
+    size_t bpo_reloc_no;
+
+    /* Set when the value is computed.  Better than coding "guard values"
+       into the other members.  Is false only for BPO relocs in a GC:ed
+       section.  */
+    boolean valid;
+  };
+
+/* We attach this as elf_section_data (sec)->tdata in the linker-allocated
+   greg contents section (MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME),
+   which is linked into the register contents section
+   (MMIX_REG_CONTENTS_SECTION_NAME).  This section is created by the
+   linker; using the same hook as for usual with BPO relocs does not
+   collide.  */
+struct bpo_greg_section_info
+  {
+    /* After GC, this reflects the number of remaining, non-excluded
+       BPO-relocs.  */
+    size_t n_bpo_relocs;
+
+    /* This is the number of allocated bpo_reloc_requests; the size of
+       sorted_indexes.  Valid after the check.*relocs functions are called
+       for all incoming sections.  It includes the number of BPO relocs in
+       sections that were GC:ed.  */
+    size_t n_max_bpo_relocs;
+
+    /* A counter used to find out when to fold the BPO gregs, since we
+       don't have a single "after-relaxation" hook.  */
+    size_t n_remaining_bpo_relocs_this_relaxation_round;
+
+    /* The number of linker-allocated GREGs resulting from BPO relocs.
+       This is an approximation after _bfd_mmix_allocated_gregs_init and
+       supposedly accurate after mmix_elf_relax_section is called for all
+       incoming non-collected sections.  */
+    size_t n_allocated_bpo_gregs;
+
+    /* Index into reloc_request[], sorted on increasing "value", secondary
+       by increasing index for strict sorting order.  */
+    size_t *bpo_reloc_indexes;
+
+    /* An array of all relocations, with the "value" member filled in by
+       the relaxation function.  */
+    struct bpo_reloc_request *reloc_request;
+  };
+
 static boolean mmix_elf_link_output_symbol_hook
   PARAMS ((bfd *, struct bfd_link_info *, const char *,
           Elf_Internal_Sym *, asection *));
@@ -61,6 +139,10 @@ static boolean mmix_elf_check_relocs
   PARAMS ((bfd *, struct bfd_link_info *, asection *,
           const Elf_Internal_Rela *));
 
+static boolean mmix_elf_check_common_relocs
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          const Elf_Internal_Rela *));
+
 static boolean mmix_elf_relocate_section
   PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
           Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
@@ -69,6 +151,10 @@ static asection * mmix_elf_gc_mark_hook
   PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
           struct elf_link_hash_entry *, Elf_Internal_Sym *));
 
+static boolean mmix_elf_gc_sweep_hook
+  PARAMS ((bfd *, struct bfd_link_info *, asection *,
+          const Elf_Internal_Rela *));
+
 static bfd_reloc_status_type mmix_final_link_relocate
   PARAMS ((reloc_howto_type *, asection *, bfd_byte *,
           bfd_vma, bfd_signed_vma, bfd_vma, const char *, asection *));
@@ -86,6 +172,12 @@ static boolean mmix_elf_add_symbol_hook
 static boolean mmix_elf_is_local_label_name
   PARAMS ((bfd *, const char *));
 
+static int bpo_reloc_request_sort_fn PARAMS ((const PTR, const PTR));
+
+static boolean mmix_elf_relax_section
+  PARAMS ((bfd *abfd, asection *sec, struct bfd_link_info *link_info,
+          boolean *again));
+
 extern boolean mmix_elf_final_link PARAMS ((bfd *, struct bfd_link_info *));
 
 extern void mmix_elf_symbol_processing PARAMS ((bfd *, asymbol *));
@@ -120,7 +212,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_8",            /* name */
         false,                 /* partial_inplace */
-        0xff,                  /* src_mask */
+        0,                     /* src_mask */
         0xff,                  /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -135,7 +227,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_16",           /* name */
         false,                 /* partial_inplace */
-        0xffff,                /* src_mask */
+        0,                     /* src_mask */
         0xffff,                /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -150,7 +242,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_24",           /* name */
         false,                 /* partial_inplace */
-        0xffffff,              /* src_mask */
+        ~0xffffff,             /* src_mask */
         0xffffff,              /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -165,7 +257,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_32",           /* name */
         false,                 /* partial_inplace */
-        0xffffffff,            /* src_mask */
+        0,                     /* src_mask */
         0xffffffff,            /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -180,7 +272,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_64",           /* name */
         false,                 /* partial_inplace */
-        MINUS_ONE,             /* src_mask */
+        0,                     /* src_mask */
         MINUS_ONE,             /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -195,7 +287,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_PC_8",         /* name */
         false,                 /* partial_inplace */
-        0xff,                  /* src_mask */
+        0,                     /* src_mask */
         0xff,                  /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -210,7 +302,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_PC_16",        /* name */
         false,                 /* partial_inplace */
-        0xffff,                /* src_mask */
+        0,                     /* src_mask */
         0xffff,                /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -225,7 +317,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_PC_24",        /* name */
         false,                 /* partial_inplace */
-        0xffffff,              /* src_mask */
+        ~0xffffff,             /* src_mask */
         0xffffff,              /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -240,7 +332,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_PC_32",        /* name */
         false,                 /* partial_inplace */
-        0xffffffff,            /* src_mask */
+        0,                     /* src_mask */
         0xffffffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -255,7 +347,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         bfd_elf_generic_reloc, /* special_function */
         "R_MMIX_PC_64",        /* name */
         false,                 /* partial_inplace */
-        MINUS_ONE,             /* src_mask */
+        0,                     /* src_mask */
         MINUS_ONE,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -303,7 +395,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_GETA",         /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -317,7 +409,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_GETA_1",               /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -331,7 +423,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_GETA_2",               /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -345,7 +437,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_GETA_3",               /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -363,7 +455,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_CBRANCH",      /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -377,7 +469,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_CBRANCH_J",    /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -391,7 +483,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_CBRANCH_1",    /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -405,7 +497,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_CBRANCH_2",    /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -419,7 +511,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_CBRANCH_3",    /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -438,7 +530,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_PUSHJ",        /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -452,7 +544,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_PUSHJ_1",      /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -466,7 +558,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_PUSHJ_2",      /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -480,7 +572,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_PUSHJ_3",      /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -498,7 +590,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_JMP",          /* name */
         false,                 /* partial_inplace */
-        0x1ffffff,             /* src_mask */
+        ~0x1ffffff,            /* src_mask */
         0x1ffffff,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -512,7 +604,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_JMP_1",        /* name */
         false,                 /* partial_inplace */
-        0x1ffffff,             /* src_mask */
+        ~0x1ffffff,            /* src_mask */
         0x1ffffff,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -526,7 +618,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_JMP_2",        /* name */
         false,                 /* partial_inplace */
-        0x1ffffff,             /* src_mask */
+        ~0x1ffffff,            /* src_mask */
         0x1ffffff,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -540,7 +632,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_JMP_3",        /* name */
         false,                 /* partial_inplace */
-        0x1ffffff,             /* src_mask */
+        ~0x1ffffff,            /* src_mask */
         0x1ffffff,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -557,7 +649,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_ADDR19",       /* name */
         false,                 /* partial_inplace */
-        0x0100ffff,            /* src_mask */
+        ~0x0100ffff,           /* src_mask */
         0x0100ffff,            /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -572,7 +664,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_ADDR27",       /* name */
         false,                 /* partial_inplace */
-        0x1ffffff,             /* src_mask */
+        ~0x1ffffff,            /* src_mask */
         0x1ffffff,             /* dst_mask */
         true),                 /* pcrel_offset */
 
@@ -588,7 +680,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_REG_OR_BYTE",  /* name */
         false,                 /* partial_inplace */
-        0xff,                  /* src_mask */
+        0,                     /* src_mask */
         0xff,                  /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -603,7 +695,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_REG",          /* name */
         false,                 /* partial_inplace */
-        0xff,                  /* src_mask */
+        0,                     /* src_mask */
         0xff,                  /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -620,7 +712,7 @@ static reloc_howto_type elf_mmix_howto_table[] =
         mmix_elf_reloc,        /* special_function */
         "R_MMIX_BASE_PLUS_OFFSET", /* name */
         false,                 /* partial_inplace */
-        0xffff,                /* src_mask */
+        0,                     /* src_mask */
         0xffff,                /* dst_mask */
         false),                /* pcrel_offset */
 
@@ -874,7 +966,7 @@ mmix_elf_perform_relocation (isec, howto, datap, addr, value)
          value >>= 2;
 
          bfd_put_32 (abfd,
-                     (in1 & ~howto->src_mask)
+                     (in1 & howto->src_mask)
                      | highbit
                      | (value & howto->dst_mask),
                      (bfd_byte *) datap);
@@ -884,6 +976,48 @@ mmix_elf_perform_relocation (isec, howto, datap, addr, value)
       else
        return bfd_reloc_overflow;
 
+    case R_MMIX_BASE_PLUS_OFFSET:
+      {
+       struct bpo_reloc_section_info *bpodata
+         = (struct bpo_reloc_section_info *)
+         elf_section_data (isec)->tdata;
+       asection *bpo_greg_section
+         = bpodata->bpo_greg_section;
+       struct bpo_greg_section_info *gregdata
+         = (struct bpo_greg_section_info *)
+         elf_section_data (bpo_greg_section)->tdata;
+       size_t bpo_index
+         = gregdata->bpo_reloc_indexes[bpodata->bpo_index++];
+
+       /* A consistency check: The value we now have in "relocation" must
+          be the same as the value we stored for that relocation.  It
+          doesn't cost much, so can be left in at all times.  */
+       if (value != gregdata->reloc_request[bpo_index].value)
+         {
+           (*_bfd_error_handler)
+             (_("%s: Internal inconsistency error for value for\n\
+ linker-allocated global register: linked: 0x%lx%08lx != relaxed: 0x%lx%08lx\n"),
+              bfd_get_filename (isec->owner),
+              (unsigned long) (value >> 32), (unsigned long) value,
+              (unsigned long) (gregdata->reloc_request[bpo_index].value
+                               >> 32),
+              (unsigned long) gregdata->reloc_request[bpo_index].value);
+           bfd_set_error (bfd_error_bad_value);
+           return bfd_reloc_overflow;
+         }
+
+       /* Then store the register number and offset for that register
+          into datap and datap + 1 respectively.  */
+       bfd_put_8 (abfd,
+                  gregdata->reloc_request[bpo_index].regindex
+                  + bpo_greg_section->output_section->vma / 8,
+                  datap);
+       bfd_put_8 (abfd,
+                  gregdata->reloc_request[bpo_index].offset,
+                  ((unsigned char *) datap) + 1);
+       return bfd_reloc_ok;
+      }
+
     case R_MMIX_REG_OR_BYTE:
     case R_MMIX_REG:
       if (value > 255)
@@ -1231,6 +1365,32 @@ mmix_final_link_relocate (howto, input_section, contents,
                                       addr, srel);
       break;
 
+    case R_MMIX_BASE_PLUS_OFFSET:
+      if (symsec == NULL)
+       return bfd_reloc_undefined;
+
+      /* Check that we're not relocating against a register symbol.  */
+      if (strcmp (bfd_get_section_name (symsec->owner, symsec),
+                 MMIX_REG_CONTENTS_SECTION_NAME) == 0
+         || strcmp (bfd_get_section_name (symsec->owner, symsec),
+                    MMIX_REG_SECTION_NAME) == 0)
+       {
+         /* Note: This is separated out into two messages in order
+            to ease the translation into other languages.  */
+         if (symname == NULL || *symname == 0)
+           (*_bfd_error_handler)
+             (_("%s: base-plus-offset relocation against register symbol: (unknown) in %s"),
+              bfd_get_filename (input_section->owner),
+              bfd_get_section_name (symsec->owner, symsec));
+         else
+           (*_bfd_error_handler)
+             (_("%s: base-plus-offset relocation against register symbol: %s in %s"),
+              bfd_get_filename (input_section->owner), symname,
+              bfd_get_section_name (symsec->owner, symsec));
+         return bfd_reloc_overflow;
+       }
+      goto do_mmix_reloc;
+
     case R_MMIX_REG_OR_BYTE:
     case R_MMIX_REG:
       /* For now, we handle these alike.  They must refer to an register
@@ -1264,7 +1424,7 @@ mmix_final_link_relocate (howto, input_section, contents,
        }
       else
        {
-         /* Note: This is seperated out into two messages in order
+         /* Note: This is separated out into two messages in order
             to ease the translation into other languages.  */
          if (symname == NULL || *symname == 0)
            (*_bfd_error_handler)
@@ -1281,6 +1441,7 @@ mmix_final_link_relocate (howto, input_section, contents,
             better value, will not get us an error.  */
          return bfd_reloc_overflow;
        }
+    do_mmix_reloc:
       contents += r_offset;
       r = mmix_elf_perform_relocation (input_section, howto, contents,
                                       addr, srel);
@@ -1396,6 +1557,37 @@ mmix_elf_gc_mark_hook (abfd, info, rel, h, sym)
 
   return NULL;
 }
+
+/* Update relocation info for a GC-excluded section.  We could supposedly
+   perform the allocation after GC, but there's no suitable hook between
+   GC (or section merge) and the point when all input sections must be
+   present.  Better to waste some memory and (perhaps) a little time.  */
+
+static boolean
+mmix_elf_gc_sweep_hook (abfd, info, sec, relocs)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info ATTRIBUTE_UNUSED;
+     asection *sec ATTRIBUTE_UNUSED;
+     const Elf_Internal_Rela *relocs ATTRIBUTE_UNUSED;
+{
+  struct bpo_reloc_section_info *bpodata
+    = (struct bpo_reloc_section_info *)
+    elf_section_data (sec)->tdata;
+  asection *allocated_gregs_section;
+
+  /* If no bpodata here, we have nothing to do.  */
+  if (bpodata == NULL)
+    return true;
+
+  allocated_gregs_section = bpodata->bpo_greg_section;
+
+  ((struct bpo_greg_section_info *)
+   elf_section_data (allocated_gregs_section)->tdata)
+    ->n_bpo_relocs
+    -= bpodata->n_bpo_relocs_this_section;
+
+  return true;
+}
 \f
 /* Sort register relocs to come before expanding relocs.  */
 
@@ -1432,6 +1624,114 @@ mmix_elf_sort_relocs (p1, p2)
   return 0;
 }
 
+/* Subset of mmix_elf_check_relocs, common to ELF and mmo linking.  */
+
+static boolean
+mmix_elf_check_common_relocs  (abfd, info, sec, relocs)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection *sec;
+     const Elf_Internal_Rela *relocs;
+{
+  bfd *bpo_greg_owner = NULL;
+  asection *allocated_gregs_section = NULL;
+  struct bpo_greg_section_info *gregdata = NULL;
+  struct bpo_reloc_section_info *bpodata = NULL;
+  const Elf_Internal_Rela *rel;
+  const Elf_Internal_Rela *rel_end;
+
+  if (info->relocateable)
+    return true;
+
+  /* We currently have to abuse this COFF-specific member, since there's
+     no target-machine-dedicated member.  There's no alternative outside
+     the bfd_link_info struct; we can't specialize a hash-table since
+     they're different between ELF and mmo.  */
+  bpo_greg_owner = (bfd *) info->base_file;
+
+  rel_end = relocs + sec->reloc_count;
+  for (rel = relocs; rel < rel_end; rel++)
+    {
+      switch (ELF64_R_TYPE (rel->r_info))
+        {
+         /* This relocation causes a GREG allocation.  We need to count
+            them, and we need to create a section for them, so we need an
+            object to fake as the owner of that section.  We can't use
+            the ELF dynobj for this, since the ELF bits assume lots of
+            DSO-related stuff if that member is non-NULL.  */
+       case R_MMIX_BASE_PLUS_OFFSET:
+         if (bpo_greg_owner == NULL)
+           {
+             bpo_greg_owner = abfd;
+             info->base_file = (PTR) bpo_greg_owner;
+           }
+
+         allocated_gregs_section
+           = bfd_get_section_by_name (bpo_greg_owner,
+                                      MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
+         if (allocated_gregs_section == NULL)
+           {
+             allocated_gregs_section
+               = bfd_make_section (bpo_greg_owner,
+                                   MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
+             /* Setting both SEC_ALLOC and SEC_LOAD means the section is
+                treated like any other section, and we'd get errors for
+                address overlap with the text section.  Let's set none of
+                those flags, as that is what currently happens for usual
+                GREG allocations, and that works.  */
+             if (allocated_gregs_section == NULL
+                 || !bfd_set_section_flags (bpo_greg_owner,
+                                            allocated_gregs_section,
+                                            (SEC_HAS_CONTENTS
+                                             | SEC_IN_MEMORY
+                                             | SEC_LINKER_CREATED))
+                 || !bfd_set_section_alignment (bpo_greg_owner,
+                                                allocated_gregs_section,
+                                                3))
+               return false;
+
+             gregdata = (struct bpo_greg_section_info *)
+               bfd_zalloc (bpo_greg_owner, sizeof (struct bpo_greg_section_info));
+             if (gregdata == NULL)
+               return false;
+             elf_section_data (allocated_gregs_section)->tdata = gregdata;
+           }
+         else if (gregdata == NULL)
+           gregdata = elf_section_data (allocated_gregs_section)->tdata;
+
+         /* Get ourselves some auxiliary info for the BPO-relocs.  */
+         if (bpodata == NULL)
+           {
+             /* No use doing a separate iteration pass to find the upper
+                limit - just use the number of relocs.  */
+             bpodata = (struct bpo_reloc_section_info *)
+               bfd_alloc (bpo_greg_owner,
+                          sizeof (struct bpo_reloc_section_info)
+                          * (sec->reloc_count + 1));
+             if (bpodata == NULL)
+               return false;
+             elf_section_data (sec)->tdata = bpodata;
+             bpodata->first_base_plus_offset_reloc
+               = bpodata->bpo_index
+               = gregdata->n_max_bpo_relocs;
+             bpodata->bpo_greg_section
+               = allocated_gregs_section;
+           }
+
+         bpodata->n_bpo_relocs_this_section++;
+         gregdata->n_max_bpo_relocs++;
+
+         /* We don't get another chance to set this before GC; we've not
+            set up set up any hook that runs before GC.  */
+         gregdata->n_bpo_relocs
+           = gregdata->n_max_bpo_relocs;
+         break;
+       }
+    }
+
+  return true;
+}
+
 /* Look through the relocs for a section during the first phase.  */
 
 static boolean
@@ -1460,6 +1760,10 @@ mmix_elf_check_relocs (abfd, info, sec, relocs)
   qsort ((PTR) relocs, sec->reloc_count, sizeof (Elf_Internal_Rela),
         mmix_elf_sort_relocs);
 
+  /* Do the common part.  */
+  if (!mmix_elf_check_common_relocs (abfd, info, sec, relocs))
+    return false;
+
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1473,7 +1777,7 @@ mmix_elf_check_relocs (abfd, info, sec, relocs)
         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
 
       switch (ELF64_R_TYPE (rel->r_info))
-        {
+       {
         /* This relocation describes the C++ object vtable hierarchy.
            Reconstruct it for later use during GC.  */
         case R_MMIX_GNU_VTINHERIT:
@@ -1487,7 +1791,48 @@ mmix_elf_check_relocs (abfd, info, sec, relocs)
           if (!_bfd_elf64_gc_record_vtentry (abfd, sec, h, rel->r_addend))
             return false;
           break;
-        }
+       }
+    }
+
+  return true;
+}
+
+/* Wrapper for mmix_elf_check_common_relocs, called when linking to mmo.
+   Copied from elf_link_add_object_symbols.  */
+
+boolean
+_bfd_mmix_check_all_relocs (abfd, info)
+     bfd *abfd;
+     struct bfd_link_info *info;
+{
+  asection *o;
+
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      Elf_Internal_Rela *internal_relocs;
+      boolean ok;
+
+      if ((o->flags & SEC_RELOC) == 0
+         || o->reloc_count == 0
+         || ((info->strip == strip_all || info->strip == strip_debugger)
+             && (o->flags & SEC_DEBUGGING) != 0)
+         || bfd_is_abs_section (o->output_section))
+       continue;
+
+      internal_relocs
+       = _bfd_elf64_link_read_relocs (abfd, o, (PTR) NULL,
+                                      (Elf_Internal_Rela *) NULL,
+                                      info->keep_memory);
+      if (internal_relocs == NULL)
+       return false;
+
+      ok = mmix_elf_check_common_relocs (abfd, info, o, internal_relocs);
+
+      if (! info->keep_memory)
+       free (internal_relocs);
+
+      if (! ok)
+       return false;
     }
 
   return true;
@@ -1685,9 +2030,459 @@ mmix_elf_final_link (abfd, info)
   if (! bfd_elf64_bfd_final_link (abfd, info))
     return false;
 
+  /* Since this section is marked SEC_LINKER_CREATED, it isn't output by
+     the regular linker machinery.  We do it here, like other targets with
+     special sections.  */
+  if (info->base_file != NULL)
+    {
+      asection *greg_section
+       = bfd_get_section_by_name ((bfd *) info->base_file,
+                                  MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
+      if (!bfd_set_section_contents (abfd,
+                                    greg_section->output_section,
+                                    greg_section->contents,
+                                    (file_ptr) greg_section->output_offset,
+                                    greg_section->_cooked_size))
+       return false;
+    }
+  return true;
+}
+
+/* Initialize stuff for the linker-generated GREGs to match
+   R_MMIX_BASE_PLUS_OFFSET relocs seen by the linker.  */
+
+boolean
+_bfd_mmix_prepare_linker_allocated_gregs (abfd, info)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *info;
+{
+  asection *bpo_gregs_section;
+  bfd *bpo_greg_owner;
+  struct bpo_greg_section_info *gregdata;
+  size_t n_gregs;
+  bfd_vma gregs_size;
+  size_t i;
+  size_t *bpo_reloc_indexes;
+
+  /* The bpo_greg_owner bfd is supposed to have been set by
+     mmix_elf_check_relocs when the first R_MMIX_BASE_PLUS_OFFSET is seen.
+     If there is no such object, there was no R_MMIX_BASE_PLUS_OFFSET.  */
+  bpo_greg_owner = (bfd *) info->base_file;
+  if (bpo_greg_owner == NULL)
+    return true;
+
+  bpo_gregs_section
+    = bfd_get_section_by_name (bpo_greg_owner,
+                              MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
+
+  /* This can't happen without DSO handling.  When DSOs are handled
+     without any R_MMIX_BASE_PLUS_OFFSET seen, there will be no such
+     section.  */
+  if (bpo_gregs_section == NULL)
+    return true;
+
+  /* We use the target-data handle in the ELF section data.  */
+  gregdata = (struct bpo_greg_section_info *)
+    elf_section_data (bpo_gregs_section)->tdata;
+  if (gregdata == NULL)
+    return false;
+
+  n_gregs = gregdata->n_bpo_relocs;
+  gregdata->n_allocated_bpo_gregs = n_gregs;
+
+  /* When this reaches zero during relaxation, all entries have been
+     filled in and the size of the linker gregs can be calculated.  */
+  gregdata->n_remaining_bpo_relocs_this_relaxation_round = n_gregs;
+
+  /* Set the zeroth-order estimate for the GREGs size.  */
+  gregs_size = n_gregs * 8;
+
+  if (!bfd_set_section_size (bpo_greg_owner, bpo_gregs_section, gregs_size))
+    return false;
+
+  /* Allocate and set up the GREG arrays.  They're filled in at relaxation
+     time.  Note that we must use the max number ever noted for the array,
+     since the index numbers were created before GC.  */
+  gregdata->reloc_request
+    = bfd_zalloc (bpo_greg_owner,
+                 sizeof (struct bpo_reloc_request)
+                 * gregdata->n_max_bpo_relocs);
+
+  gregdata->bpo_reloc_indexes
+    = bpo_reloc_indexes
+    = bfd_alloc (bpo_greg_owner,
+                gregdata->n_max_bpo_relocs
+                * sizeof (size_t));
+  if (bpo_reloc_indexes == NULL)
+    return false;
+
+  /* The default order is an identity mapping.  */
+  for (i = 0; i < gregdata->n_max_bpo_relocs; i++)
+    {
+      bpo_reloc_indexes[i] = i;
+      gregdata->reloc_request[i].bpo_reloc_no = i;
+    }
+
   return true;
 }
 \f
+/* Fill in contents in the linker allocated gregs.  Everything is
+   calculated at this point; we just move the contents into place here.  */
+
+boolean
+_bfd_mmix_finalize_linker_allocated_gregs (abfd, link_info)
+     bfd *abfd ATTRIBUTE_UNUSED;
+     struct bfd_link_info *link_info;
+{
+  asection *bpo_gregs_section;
+  bfd *bpo_greg_owner;
+  struct bpo_greg_section_info *gregdata;
+  size_t n_gregs;
+  size_t i, j;
+  size_t lastreg;
+  bfd_byte *contents;
+
+  /* The bpo_greg_owner bfd is supposed to have been set by mmix_elf_check_relocs
+     when the first R_MMIX_BASE_PLUS_OFFSET is seen.  If there is no such
+     object, there was no R_MMIX_BASE_PLUS_OFFSET.  */
+  bpo_greg_owner = (bfd *) link_info->base_file;
+  if (bpo_greg_owner == NULL)
+    return true;
+
+  bpo_gregs_section
+    = bfd_get_section_by_name (bpo_greg_owner,
+                              MMIX_LD_ALLOCATED_REG_CONTENTS_SECTION_NAME);
+
+  /* This can't happen without DSO handling.  When DSOs are handled
+     without any R_MMIX_BASE_PLUS_OFFSET seen, there will be no such
+     section.  */
+  if (bpo_gregs_section == NULL)
+    return true;
+
+  /* We use the target-data handle in the ELF section data.  */
+
+  gregdata = (struct bpo_greg_section_info *)
+    elf_section_data (bpo_gregs_section)->tdata;
+  if (gregdata == NULL)
+    return false;
+
+  n_gregs = gregdata->n_allocated_bpo_gregs;
+
+  bpo_gregs_section->contents
+    = contents = bfd_alloc (bpo_greg_owner, bpo_gregs_section->_cooked_size);
+  if (contents == NULL)
+    return false;
+
+  for (lastreg = 255, i = 0, j = 0; j < n_gregs; i++)
+    if (gregdata->reloc_request[i].regindex != lastreg)
+      {
+       bfd_put_64 (bpo_greg_owner, gregdata->reloc_request[i].value,
+                   contents + j * 8);
+       lastreg = gregdata->reloc_request[i].regindex;
+       j++;
+      }
+
+  return true;
+}
+
+/* Sort valid relocs to come before non-valid relocs, then on increasing
+   value.  */
+
+static int
+bpo_reloc_request_sort_fn (p1, p2)
+     const PTR p1;
+     const PTR p2;
+{
+  const struct bpo_reloc_request *r1 = (const struct bpo_reloc_request *) p1;
+  const struct bpo_reloc_request *r2 = (const struct bpo_reloc_request *) p2;
+
+  /* Primary function is validity; non-valid relocs sorted after valid
+     ones.  */
+  if (r1->valid != r2->valid)
+    return r2->valid - r1->valid;
+
+  /* Then sort on value.  */
+  if (r1->value != r2->value)
+    return r1->value - r2->value;
+
+  /* As a last re-sort, use the address so we get a stable sort.  */
+  return r1 > r2 ? 1 : (r1 < r2 ? -1 : 0);
+}
+
+/* This links all R_MMIX_BASE_PLUS_OFFSET relocs into a special array, and
+   when the last such reloc is done, an index-array is sorted according to
+   the values and iterated over to produce register numbers (indexed by 0
+   from the first allocated register number) and offsets for use in real
+   relocation.
+
+   Symbol- and reloc-reading infrastructure copied from elf-m10200.c.  */
+
+static boolean
+mmix_elf_relax_section (abfd, sec, link_info, again)
+     bfd *abfd;
+     asection *sec;
+     struct bfd_link_info *link_info;
+     boolean *again;
+{
+
+  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Shdr *shndx_hdr;
+  Elf_Internal_Rela *internal_relocs;
+  Elf_Internal_Rela *free_relocs = NULL;
+  Elf_Internal_Rela *irel, *irelend;
+  asection *bpo_gregs_section = NULL;
+  struct bpo_greg_section_info *gregdata;
+  struct bpo_reloc_section_info *bpodata
+    = (struct bpo_reloc_section_info *)
+    elf_section_data (sec)->tdata;
+  size_t bpono;
+  bfd *bpo_greg_owner;
+  Elf64_External_Sym *extsyms = NULL;
+  Elf64_External_Sym *free_extsyms = NULL;
+  Elf_External_Sym_Shndx *shndx_buf = NULL;
+
+  /* Assume nothing changes.  */
+  *again = false;
+
+  /* If this is the first time we have been called for this section,
+     initialize the cooked size.  */
+  if (sec->_cooked_size == 0)
+    sec->_cooked_size = sec->_raw_size;
+
+  /* We don't have to do anything for a relocateable link, if
+     this section does not have relocs, or if this is not a
+     code section.  */
+  if (link_info->relocateable
+      || (sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0
+      || (sec->flags & SEC_CODE) == 0
+      || (sec->flags & SEC_LINKER_CREATED) != 0
+      /* If no R_MMIX_BASE_PLUS_OFFSET relocs, then nothing to do.  */
+      || bpodata == NULL)
+    return true;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+
+  bpo_greg_owner = (bfd *) link_info->base_file;
+  bpo_gregs_section = bpodata->bpo_greg_section;
+  gregdata = (struct bpo_greg_section_info *)
+    elf_section_data (bpo_gregs_section)->tdata;
+
+  bpono = bpodata->first_base_plus_offset_reloc;
+
+  /* Get a copy of the native relocations.  */
+  internal_relocs
+    = _bfd_elf64_link_read_relocs (abfd, sec, (PTR) NULL,
+                                  (Elf_Internal_Rela *) NULL,
+                                  link_info->keep_memory);
+  if (internal_relocs == NULL)
+    goto error_return;
+  if (! link_info->keep_memory)
+    free_relocs = internal_relocs;
+
+  /* Walk through them looking for relaxing opportunities.  */
+  irelend = internal_relocs + sec->reloc_count;
+  for (irel = internal_relocs; irel < irelend; irel++)
+    {
+      bfd_vma symval;
+
+      if (ELF64_R_TYPE (irel->r_info) != (int) R_MMIX_BASE_PLUS_OFFSET)
+       continue;
+
+      /* Read this BFD's symbols if we haven't done so already.  */
+      if (extsyms == NULL)
+       {
+         /* Get cached copy if it exists.  */
+         if (symtab_hdr->contents != NULL)
+           extsyms = (Elf64_External_Sym *) symtab_hdr->contents;
+         else
+           {
+             /* Go get them off disk.  */
+             bfd_size_type amt;
+
+             amt = symtab_hdr->sh_info;
+             amt *= sizeof (Elf64_External_Sym);
+             extsyms = (Elf64_External_Sym *) bfd_malloc (amt);
+             if (extsyms == NULL)
+               goto error_return;
+             free_extsyms = extsyms;
+             if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
+                 || bfd_bread ((PTR) extsyms, amt, abfd) != amt)
+               goto error_return;
+             symtab_hdr->contents = (bfd_byte *) extsyms;
+           }
+
+         /* If >64k sections, this presumable happens.  No test-case.  */
+         if (shndx_hdr->sh_size != 0)
+           {
+             bfd_size_type amt;
+
+             amt = symtab_hdr->sh_info;
+             amt *= sizeof (Elf_External_Sym_Shndx);
+             shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+             if (shndx_buf == NULL)
+               goto error_return;
+             if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+                 || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+               goto error_return;
+             shndx_hdr->contents = (bfd_byte *) shndx_buf;
+           }
+       }
+
+      /* Get the value of the symbol referred to by the reloc.  */
+      if (ELF64_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+       {
+         /* A local symbol.  */
+         Elf64_External_Sym *esym;
+         Elf_External_Sym_Shndx *shndx;
+         Elf_Internal_Sym isym;
+         asection *sym_sec;
+
+         esym = extsyms + ELF64_R_SYM (irel->r_info);
+         shndx = shndx_buf + (shndx_buf
+                              ? ELF64_R_SYM (irel->r_info) : 0);
+         bfd_elf64_swap_symbol_in (abfd, esym, shndx, &isym);
+
+         if (isym.st_shndx == SHN_UNDEF)
+           sym_sec = bfd_und_section_ptr;
+         else if (isym.st_shndx == SHN_ABS)
+           sym_sec = bfd_abs_section_ptr;
+         else if (isym.st_shndx == SHN_COMMON)
+           sym_sec = bfd_com_section_ptr;
+         else
+           sym_sec = bfd_section_from_elf_index (abfd, isym.st_shndx);
+         symval = (isym.st_value
+                   + sym_sec->output_section->vma
+                   + sym_sec->output_offset);
+       }
+      else
+       {
+         unsigned long indx;
+         struct elf_link_hash_entry *h;
+
+         /* An external symbol.  */
+         indx = ELF64_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+         h = elf_sym_hashes (abfd)[indx];
+         BFD_ASSERT (h != NULL);
+         if (h->root.type != bfd_link_hash_defined
+             && h->root.type != bfd_link_hash_defweak)
+           {
+             /* This appears to be a reference to an undefined
+                 symbol.  Just ignore it--it will be caught by the
+                 regular reloc processing.  */
+             continue;
+           }
+
+         symval = (h->root.u.def.value
+                   + h->root.u.def.section->output_section->vma
+                   + h->root.u.def.section->output_offset);
+       }
+
+      gregdata->reloc_request[gregdata->bpo_reloc_indexes[bpono]].value
+       = symval + irel->r_addend;
+      gregdata->reloc_request[gregdata->bpo_reloc_indexes[bpono++]].valid = true;
+      gregdata->n_remaining_bpo_relocs_this_relaxation_round--;
+    }
+
+  /* Check if that was the last BPO-reloc.  If so, sort the values and
+     calculate how many registers we need to cover them.  Set the size of
+     the linker gregs, and if the number of registers changed, indicate
+     that we need to relax some more because we have more work to do.  */
+  if (gregdata->n_remaining_bpo_relocs_this_relaxation_round == 0)
+    {
+      size_t i;
+      bfd_vma prev_base;
+      size_t regindex;
+
+      /* First, reset the remaining relocs for the next round.  */
+      gregdata->n_remaining_bpo_relocs_this_relaxation_round
+       = gregdata->n_bpo_relocs;
+
+      qsort ((PTR) gregdata->reloc_request,
+            gregdata->n_max_bpo_relocs,
+            sizeof (struct bpo_reloc_request),
+            bpo_reloc_request_sort_fn);
+
+      /* Recalculate indexes.  When we find a change (however unlikely
+        after the initial iteration), we know we need to relax again,
+        since items in the GREG-array are sorted by increasing value and
+        stored in the relaxation phase.  */
+      for (i = 0; i < gregdata->n_max_bpo_relocs; i++)
+       if (gregdata->bpo_reloc_indexes[gregdata->reloc_request[i].bpo_reloc_no]
+           != i)
+         {
+           gregdata->bpo_reloc_indexes[gregdata->reloc_request[i].bpo_reloc_no]
+             = i;
+           *again = true;
+         }
+
+      /* Allocate register numbers (indexing from 0).  Stop at the first
+        non-valid reloc.  */
+      for (i = 0, regindex = 0, prev_base = gregdata->reloc_request[0].value;
+          i < gregdata->n_bpo_relocs;
+          i++)
+       {
+         if (gregdata->reloc_request[i].value > prev_base + 255)
+           {
+             regindex++;
+             prev_base = gregdata->reloc_request[i].value;
+           }
+         gregdata->reloc_request[i].regindex = regindex;
+         gregdata->reloc_request[i].offset
+           = gregdata->reloc_request[i].value - prev_base;
+       }
+
+      /* If it's not the same as the last time, we need to relax again,
+        because the size of the section has changed.  I'm not sure we
+        actually need to do any adjustments since the shrinking happens
+        at the start of this section, but better safe than sorry.  */
+      if (gregdata->n_allocated_bpo_gregs != regindex + 1)
+       {
+         gregdata->n_allocated_bpo_gregs = regindex + 1;
+         *again = true;
+       }
+
+      bpo_gregs_section->_cooked_size = (regindex + 1) * 8;
+    }
+
+  if (free_relocs != NULL)
+    free (free_relocs);
+
+  if (shndx_buf != NULL)
+    {
+      shndx_hdr->contents = NULL;
+      free (shndx_buf);
+    }
+
+  if (free_extsyms != NULL)
+    {
+      if (! link_info->keep_memory)
+       {
+         symtab_hdr->contents = NULL;
+         free (free_extsyms);
+       }
+    }
+
+  return true;
+
+ error_return:
+  if (free_relocs != NULL)
+    free (free_relocs);
+  if (shndx_buf != NULL)
+    {
+      shndx_hdr->contents = NULL;
+      free (shndx_buf);
+    }
+  if (free_extsyms != NULL)
+    {
+      symtab_hdr->contents = NULL;
+      free (free_extsyms);
+    }
+
+  return false;
+}
+\f
 #define ELF_ARCH               bfd_arch_mmix
 #define ELF_MACHINE_CODE       EM_MMIX
 
@@ -1712,6 +2507,8 @@ mmix_elf_final_link (abfd, info)
 #define elf_info_to_howto              mmix_info_to_howto_rela
 #define elf_backend_relocate_section   mmix_elf_relocate_section
 #define elf_backend_gc_mark_hook       mmix_elf_gc_mark_hook
+#define elf_backend_gc_sweep_hook      mmix_elf_gc_sweep_hook
+
 #define elf_backend_link_output_symbol_hook \
        mmix_elf_link_output_symbol_hook
 #define elf_backend_add_symbol_hook    mmix_elf_add_symbol_hook
@@ -1731,5 +2528,6 @@ mmix_elf_final_link (abfd, info)
        mmix_elf_section_from_bfd_section
 
 #define bfd_elf64_bfd_final_link       mmix_elf_final_link
+#define bfd_elf64_bfd_relax_section    mmix_elf_relax_section
 
 #include "elf64-target.h"
index 019160c3386d8d44c580b61b74ea1c10b54b343d..ae7c42c4b4544c5451854c854c57b50a57cdac1f 100644 (file)
--- a/bfd/mmo.c
+++ b/bfd/mmo.c
@@ -201,7 +201,6 @@ EXAMPLE
 #include "libiberty.h"
 #include "elf/mmix.h"
 #include "opcode/mmix.h"
-#include <ctype.h>
 
 #define LOP 0x98
 #define LOP_QUOTE 0
@@ -481,10 +480,9 @@ mmo_init ()
   static const char letters[]
     = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789:_";
 
-  if (inited == false)
-    {
-      inited = true;
-    }
+  if (inited == true)
+    return;
+  inited = true;
 
   /* Fill in the set of valid symbol characters.  */
   strcpy (valid_mmo_symbol_character_set, letters);