gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / bfd / elf32-frv.c
index d95fa800c8756a1f99055a7132f1b70a12adaff1..83de5e67b5f30f10b2d8f85f156314074b84ae1b 100644 (file)
@@ -1,94 +1,45 @@
 /* FRV-specific support for 32-bit ELF.
-   Copyright 2002, 2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 2002-2020 Free Software Foundation, Inc.
 
-This file is part of BFD, the Binary File Descriptor library.
+   This file is part of BFD, the Binary File Descriptor library.
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
-#include "bfd.h"
 #include "sysdep.h"
+#include "bfd.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
 #include "elf/frv.h"
-#include "elf/dwarf2.h"
+#include "dwarf2.h"
 #include "hashtab.h"
+#include "libiberty.h"
 
 /* Forward declarations.  */
-static bfd_reloc_status_type elf32_frv_relocate_lo16
-  PARAMS ((bfd *,  Elf_Internal_Rela *, bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_hi16
-  PARAMS ((bfd *,  Elf_Internal_Rela *, bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_label24
-  PARAMS ((bfd *, asection *, Elf_Internal_Rela *, bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_gprel12
-  PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *,
-          bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_gprelu12
-  PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *,
-          bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_gprello
-  PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *,
-          bfd_byte *, bfd_vma));
-static bfd_reloc_status_type elf32_frv_relocate_gprelhi
-  PARAMS ((struct bfd_link_info *, bfd *, asection *, Elf_Internal_Rela *,
-          bfd_byte *, bfd_vma));
-static reloc_howto_type *frv_reloc_type_lookup
-  PARAMS ((bfd *, bfd_reloc_code_real_type));
-static void frv_info_to_howto_rela
-  PARAMS ((bfd *, arelent *, Elf_Internal_Rela *));
-static bfd_boolean elf32_frv_relocate_section
-  PARAMS ((bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, Elf_Internal_Sym *, asection **));
-static bfd_boolean elf32_frv_add_symbol_hook
-  PARAMS (( bfd *, struct bfd_link_info *, Elf_Internal_Sym *,
-           const char **, flagword *, asection **, bfd_vma *));
-static bfd_reloc_status_type frv_final_link_relocate
-  PARAMS ((reloc_howto_type *, bfd *, asection *, bfd_byte *,
-          Elf_Internal_Rela *, bfd_vma));
-static bfd_boolean elf32_frv_gc_sweep_hook
-  PARAMS ((bfd *, struct bfd_link_info *, asection *, const
-          Elf_Internal_Rela *));
-static asection * elf32_frv_gc_mark_hook
-  PARAMS ((asection *, struct bfd_link_info *, Elf_Internal_Rela *,
-          struct elf_link_hash_entry *, Elf_Internal_Sym *));
-static bfd_boolean elf32_frv_check_relocs
-  PARAMS ((bfd *, struct bfd_link_info *, asection *,
-          const Elf_Internal_Rela *));
-static int elf32_frv_machine
-  PARAMS ((bfd *));
-static bfd_boolean elf32_frv_object_p
-  PARAMS ((bfd *));
-static bfd_boolean frv_elf_set_private_flags
-  PARAMS ((bfd *, flagword));
-static bfd_boolean frv_elf_copy_private_bfd_data
-  PARAMS ((bfd *, bfd *));
-static bfd_boolean frv_elf_merge_private_bfd_data
-  PARAMS ((bfd *, bfd *));
-static bfd_boolean frv_elf_print_private_bfd_data
-  PARAMS ((bfd *, PTR));
+
 
 static reloc_howto_type elf32_frv_howto_table [] =
 {
   /* This reloc does nothing.  */
   HOWTO (R_FRV_NONE,           /* type */
         0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        3,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_NONE",          /* name */
         FALSE,                 /* partial_inplace */
@@ -112,7 +63,7 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE),                /* pcrel_offset */
 
   /* A 16 bit pc-relative relocation.  */
-  HOWTO (R_FRV_LABEL16,                /* type */
+  HOWTO (R_FRV_LABEL16,                /* type */
         2,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -124,10 +75,10 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 
   /* A 24-bit pc-relative relocation.  */
-  HOWTO (R_FRV_LABEL24,        /* type */
+  HOWTO (R_FRV_LABEL24,                /* type */
         2,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         26,                    /* bitsize */
@@ -139,9 +90,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0x7e03ffff,            /* src_mask */
         0x7e03ffff,            /* dst_mask */
-        TRUE),                 /* pcrel_offset */
+        TRUE),                 /* pcrel_offset */
 
-  HOWTO (R_FRV_LO16,           /* type */
+  HOWTO (R_FRV_LO16,           /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -153,9 +104,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_HI16,           /* type */
+  HOWTO (R_FRV_HI16,           /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -167,9 +118,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_GPREL12,                /* type */
+  HOWTO (R_FRV_GPREL12,                /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         12,                    /* bitsize */
@@ -177,13 +128,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_FRV_GPREL12",       /* name */
+        "R_FRV_GPREL12",       /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0xfff,                 /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_GPRELU12,        /* type */
+  HOWTO (R_FRV_GPRELU12,       /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         12,                    /* bitsize */
@@ -191,13 +142,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_FRV_GPRELU12",      /* name */
+        "R_FRV_GPRELU12",      /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0x3f03f,               /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0x3f03f,               /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_GPREL32,         /* type */
+  HOWTO (R_FRV_GPREL32,                /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         32,                    /* bitsize */
@@ -207,11 +158,11 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GPREL32",       /* name */
         FALSE,                 /* partial_inplace */
-        0xffffffff,            /* src_mask */
+        0xffffffff,            /* src_mask */
         0xffffffff,            /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_GPRELHI,                /* type */
+  HOWTO (R_FRV_GPRELHI,                /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -221,11 +172,11 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GPRELHI",       /* name */
         FALSE,                 /* partial_inplace */
-        0xffff,                        /* src_mask */
+        0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
-  HOWTO (R_FRV_GPRELLO,                /* type */
+  HOWTO (R_FRV_GPRELLO,                /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -235,13 +186,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GPRELLO",       /* name */
         FALSE,                 /* partial_inplace */
-        0xffff,                        /* src_mask */
+        0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 12-bit signed operand with the GOT offset for the address of
      the symbol.  */
-  HOWTO (R_FRV_GOT12,          /* type */
+  HOWTO (R_FRV_GOT12,          /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         12,                    /* bitsize */
@@ -251,13 +202,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GOT12",         /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0xfff,                 /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
   /* The upper 16 bits of the GOT offset for the address of the
      symbol.  */
-  HOWTO (R_FRV_GOTHI,          /* type */
+  HOWTO (R_FRV_GOTHI,          /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -267,13 +218,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GOTHI",         /* name */
         FALSE,                 /* partial_inplace */
-        0xffff,                        /* src_mask */
+        0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* The lower 16 bits of the GOT offset for the address of the
      symbol.  */
-  HOWTO (R_FRV_GOTLO,          /* type */
+  HOWTO (R_FRV_GOTLO,          /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -285,7 +236,7 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* The 32-bit address of the canonical descriptor of a function.  */
   HOWTO (R_FRV_FUNCDESC,       /* type */
@@ -314,9 +265,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_FUNCDESC_GOT12", /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0xfff,                 /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
   /* The upper 16 bits of the GOT offset for the address of the
      canonical descriptor of a function.  */
@@ -332,7 +283,7 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* The lower 16 bits of the GOT offset for the address of the
      canonical descriptor of a function.  */
@@ -348,9 +299,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
-  /* The 32-bit address of the canonical descriptor of a function.  */
+  /* The 64-bit descriptor of a function.  */
   HOWTO (R_FRV_FUNCDESC_VALUE, /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -377,9 +328,9 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_FUNCDESC_GOTOFF12", /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0xfff,                 /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
   /* The upper 16 bits of the GOT offset for the address of the
      canonical descriptor of a function.  */
@@ -395,7 +346,7 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* The lower 16 bits of the GOT offset for the address of the
      canonical descriptor of a function.  */
@@ -411,11 +362,11 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* A 12-bit signed operand with the GOT offset for the address of
      the symbol.  */
-  HOWTO (R_FRV_GOTOFF12,        /* type */
+  HOWTO (R_FRV_GOTOFF12,       /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         12,                    /* bitsize */
@@ -425,13 +376,13 @@ static reloc_howto_type elf32_frv_howto_table [] =
         bfd_elf_generic_reloc, /* special_function */
         "R_FRV_GOTOFF12",      /* name */
         FALSE,                 /* partial_inplace */
-        0xfff,                 /* src_mask */
-        0xfff,                 /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
   /* The upper 16 bits of the GOT offset for the address of the
      symbol.  */
-  HOWTO (R_FRV_GOTOFFHI,        /* type */
+  HOWTO (R_FRV_GOTOFFHI,       /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -443,7 +394,7 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
 
   /* The lower 16 bits of the GOT offset for the address of the
      symbol.  */
@@ -459,41 +410,300 @@ static reloc_howto_type elf32_frv_howto_table [] =
         FALSE,                 /* partial_inplace */
         0xffff,                /* src_mask */
         0xffff,                /* dst_mask */
-        FALSE),                /* pcrel_offset */
+        FALSE),                /* pcrel_offset */
+
+  /* A 24-bit pc-relative relocation referencing the TLS PLT entry for
+     a thread-local symbol.  If the symbol number is 0, it refers to
+     the module.  */
+  HOWTO (R_FRV_GETTLSOFF,      /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        26,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GETTLSOFF",     /* name */
+        FALSE,                 /* partial_inplace */
+        0x7e03ffff,            /* src_mask */
+        0x7e03ffff,            /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* A 64-bit TLS descriptor for a symbol.  This relocation is only
+     valid as a REL, dynamic relocation.  */
+  HOWTO (R_FRV_TLSDESC_VALUE,  /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSDESC_VALUE", /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* A 12-bit signed operand with the GOT offset for the TLS
+     descriptor of the symbol.  */
+  HOWTO (R_FRV_GOTTLSDESC12,   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        12,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSDESC12",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The upper 16 bits of the GOT offset for the TLS descriptor of the
+     symbol.  */
+  HOWTO (R_FRV_GOTTLSDESCHI,   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSDESCHI",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The lower 16 bits of the GOT offset for the TLS descriptor of the
+     symbol.  */
+  HOWTO (R_FRV_GOTTLSDESCLO,   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSDESCLO",  /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* A 12-bit signed operand with the offset from the module base
+     address to the thread-local symbol address.  */
+  HOWTO (R_FRV_TLSMOFF12,       /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        12,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSMOFF12",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The upper 16 bits of the offset from the module base address to
+     the thread-local symbol address.  */
+  HOWTO (R_FRV_TLSMOFFHI,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSMOFFHI",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The lower 16 bits of the offset from the module base address to
+     the thread-local symbol address.  */
+  HOWTO (R_FRV_TLSMOFFLO,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSMOFFLO",     /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* A 12-bit signed operand with the GOT offset for the TLSOFF entry
+     for a symbol.  */
+  HOWTO (R_FRV_GOTTLSOFF12,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        12,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSOFF12",   /* name */
+        FALSE,                 /* partial_inplace */
+        0xfff,                 /* src_mask */
+        0xfff,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The upper 16 bits of the GOT offset for the TLSOFF entry for a
+     symbol.  */
+  HOWTO (R_FRV_GOTTLSOFFHI,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSOFFHI",   /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The lower 16 bits of the GOT offset for the TLSOFF entry for a
+     symbol.  */
+  HOWTO (R_FRV_GOTTLSOFFLO,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GOTTLSOFFLO",   /* name */
+        FALSE,                 /* partial_inplace */
+        0xffff,                /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The 32-bit offset from the thread pointer (not the module base
+     address) to a thread-local symbol.  */
+  HOWTO (R_FRV_TLSOFF,         /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSOFF",        /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* An annotation for linker relaxation, that denotes the
+     symbol+addend whose TLS descriptor is referenced by the sum of
+     the two input registers of an ldd instruction.  */
+  HOWTO (R_FRV_TLSDESC_RELAX,  /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSDESC_RELAX", /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* An annotation for linker relaxation, that denotes the
+     symbol+addend whose TLS resolver entry point is given by the sum
+     of the two register operands of an calll instruction.  */
+  HOWTO (R_FRV_GETTLSOFF_RELAX,        /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_GETTLSOFF_RELAX", /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* An annotation for linker relaxation, that denotes the
+     symbol+addend whose TLS offset GOT entry is given by the sum of
+     the two input registers of an ld instruction.  */
+  HOWTO (R_FRV_TLSOFF_RELAX,   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSOFF_RELAX",  /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
+  /* A 32-bit offset from the module base address to
+     the thread-local symbol address.  */
+  HOWTO (R_FRV_TLSMOFF,                /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSMOFF",       /* name */
+        FALSE,                 /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
 };
 
 /* GNU extension to record C++ vtable hierarchy.  */
 static reloc_howto_type elf32_frv_vtinherit_howto =
-  HOWTO (R_FRV_GNU_VTINHERIT,   /* type */
-         0,                     /* rightshift */
-         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-         0,                     /* bitsize */
-         FALSE,                 /* pc_relative */
-         0,                     /* bitpos */
-         complain_overflow_dont, /* complain_on_overflow */
-         NULL,                  /* special_function */
-         "R_FRV_GNU_VTINHERIT", /* name */
-         FALSE,                 /* partial_inplace */
-         0,                     /* src_mask */
-         0,                     /* dst_mask */
-         FALSE);                /* pcrel_offset */
+  HOWTO (R_FRV_GNU_VTINHERIT,  /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        NULL,                  /* special_function */
+        "R_FRV_GNU_VTINHERIT", /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE);                /* pcrel_offset */
 
   /* GNU extension to record C++ vtable member usage.  */
 static reloc_howto_type elf32_frv_vtentry_howto =
-  HOWTO (R_FRV_GNU_VTENTRY,     /* type */
-         0,                     /* rightshift */
-         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-         0,                     /* bitsize */
-         FALSE,                 /* pc_relative */
-         0,                     /* bitpos */
-         complain_overflow_dont, /* complain_on_overflow */
-         _bfd_elf_rel_vtable_reloc_fn,  /* special_function */
-         "R_FRV_GNU_VTENTRY",   /* name */
-         FALSE,                 /* partial_inplace */
-         0,                     /* src_mask */
-         0,                     /* dst_mask */
-         FALSE);                /* pcrel_offset */
+  HOWTO (R_FRV_GNU_VTENTRY,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        _bfd_elf_rel_vtable_reloc_fn, /* special_function */
+        "R_FRV_GNU_VTENTRY",   /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE);                /* pcrel_offset */
 
 /* The following 3 relocations are REL.  The only difference to the
    entries in the table above are that partial_inplace is TRUE.  */
@@ -542,97 +752,189 @@ static reloc_howto_type elf32_frv_rel_funcdesc_value_howto =
         0xffffffff,            /* dst_mask */
         FALSE);                /* pcrel_offset */
 
-\f
-/* Map BFD reloc types to FRV ELF reloc types.  */
-#if 0
-struct frv_reloc_map
-{
-  unsigned int bfd_reloc_val;
-  unsigned int frv_reloc_val;
-};
-
-static const struct frv_reloc_map frv_reloc_map [] =
-{
-  { BFD_RELOC_NONE,           R_FRV_NONE },
-  { BFD_RELOC_32,             R_FRV_32 },
-  { BFD_RELOC_FRV_LABEL16,    R_FRV_LABEL16 },
-  { BFD_RELOC_FRV_LABEL24,    R_FRV_LABEL24 },
-  { BFD_RELOC_FRV_LO16,       R_FRV_LO16 },
-  { BFD_RELOC_FRV_HI16,       R_FRV_HI16 },
-  { BFD_RELOC_FRV_GPREL12,    R_FRV_GPREL12 },
-  { BFD_RELOC_FRV_GPRELU12,   R_FRV_GPRELU12 },
-  { BFD_RELOC_FRV_GPREL32,    R_FRV_GPREL32 },
-  { BFD_RELOC_FRV_GPRELHI,    R_FRV_GPRELHI },
-  { BFD_RELOC_FRV_GPRELLO,    R_FRV_GPRELLO },
-  { BFD_RELOC_FRV_GOT12,      R_FRV_GOT12 },
-  { BFD_RELOC_FRV_GOTHI,      R_FRV_GOTHI },
-  { BFD_RELOC_FRV_GOTLO,      R_FRV_GOTLO },
-  { BFD_RELOC_FRV_FUNCDESC,   R_FRV_FUNCDESC },
-  { BFD_RELOC_FRV_FUNCDESC_GOT12, R_FRV_FUNCDESC_GOT12 },
-  { BFD_RELOC_FRV_FUNCDESC_GOTHI, R_FRV_FUNCDESC_GOTHI },
-  { BFD_RELOC_FRV_FUNCDESC_GOTLO, R_FRV_FUNCDESC_GOTLO },
-  { BFD_RELOC_FRV_FUNCDESC_VALUE, R_FRV_FUNCDESC_VALUE },
-  { BFD_RELOC_FRV_FUNCDESC_GOTOFF12, R_FRV_FUNCDESC_GOTOFF12 },
-  { BFD_RELOC_FRV_FUNCDESC_GOTOFFHI, R_FRV_FUNCDESC_GOTOFFHI },
-  { BFD_RELOC_FRV_FUNCDESC_GOTOFFLO, R_FRV_FUNCDESC_GOTOFFLO },
-  { BFD_RELOC_FRV_GOTOFF12,   R_FRV_GOTOFF12 },
-  { BFD_RELOC_FRV_GOTOFFHI,   R_FRV_GOTOFFHI },
-  { BFD_RELOC_FRV_GOTOFFLO,   R_FRV_GOTOFFLO },
-  { BFD_RELOC_VTABLE_INHERIT, R_FRV_GNU_VTINHERIT },
-  { BFD_RELOC_VTABLE_ENTRY,   R_FRV_GNU_VTENTRY },
-};
-#endif
-
-extern const bfd_target bfd_elf32_frvfdpic_vec;
-#define IS_FDPIC(bfd) ((bfd)->xvec == &bfd_elf32_frvfdpic_vec)
+static reloc_howto_type elf32_frv_rel_tlsdesc_value_howto =
+  /* A 64-bit TLS descriptor for a symbol.  The first word resolves to
+     an entry point, and the second resolves to a special argument.
+     If the symbol turns out to be in static TLS, the entry point is a
+     return instruction, and the special argument is the TLS offset
+     for the symbol.  If it's in dynamic TLS, the entry point is a TLS
+     offset resolver, and the special argument is a pointer to a data
+     structure allocated by the dynamic loader, containing the GOT
+     address for the offset resolver, the module id, the offset within
+     the module, and anything else the TLS offset resolver might need
+     to determine the TLS offset for the symbol in the running
+     thread.  */
+  HOWTO (R_FRV_TLSDESC_VALUE,  /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSDESC_VALUE", /* name */
+        TRUE,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE);                /* pcrel_offset */
 
-/* An extension of the elf hash table data structure, containing some
-   additional FRV-specific data.  */
-struct frvfdpic_elf_link_hash_table
+static reloc_howto_type elf32_frv_rel_tlsoff_howto =
+  /* The 32-bit offset from the thread pointer (not the module base
+     address) to a thread-local symbol.  */
+  HOWTO (R_FRV_TLSOFF,         /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_FRV_TLSOFF",        /* name */
+        TRUE,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE);                /* pcrel_offset */
+
+
+\f
+extern const bfd_target frv_elf32_fdpic_vec;
+#define IS_FDPIC(bfd) ((bfd)->xvec == &frv_elf32_fdpic_vec)
+
+/* An extension of the elf hash table data structure, containing some
+   additional FRV-specific data.  */
+struct frvfdpic_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
 
-  /* A pointer to the .got section.  */
-  asection *sgot;
-  /* A pointer to the .rel.got section.  */
-  asection *sgotrel;
   /* A pointer to the .rofixup section.  */
   asection *sgotfixup;
-  /* A pointer to the .plt section.  */
-  asection *splt;
-  /* A pointer to the .rel.plt section.  */
-  asection *spltrel;
   /* GOT base offset.  */
   bfd_vma got0;
   /* Location of the first non-lazy PLT entry, i.e., the number of
-     bytes taken by lazy PLT entries.  */
+     bytes taken by lazy PLT entries.  If locally-bound TLS
+     descriptors require a ret instruction, it will be placed at this
+     offset.  */
   bfd_vma plt0;
   /* A hash table holding information about which symbols were
      referenced with which PIC-related relocations.  */
   struct htab *relocs_info;
+  /* Summary reloc information collected by
+     _frvfdpic_count_got_plt_entries.  */
+  struct _frvfdpic_dynamic_got_info *g;
 };
 
 /* Get the FRV ELF linker hash table from a link_info structure.  */
 
-#define frvfdpic_hash_table(info) \
-  ((struct frvfdpic_elf_link_hash_table *) ((info)->hash))
+#define frvfdpic_hash_table(p) \
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \
+  == FRV_ELF_DATA ? ((struct frvfdpic_elf_link_hash_table *) ((p)->hash)) : NULL)
 
 #define frvfdpic_got_section(info) \
-  (frvfdpic_hash_table (info)->sgot)
+  (frvfdpic_hash_table (info)->elf.sgot)
 #define frvfdpic_gotrel_section(info) \
-  (frvfdpic_hash_table (info)->sgotrel)
+  (frvfdpic_hash_table (info)->elf.srelgot)
 #define frvfdpic_gotfixup_section(info) \
   (frvfdpic_hash_table (info)->sgotfixup)
 #define frvfdpic_plt_section(info) \
-  (frvfdpic_hash_table (info)->splt)
+  (frvfdpic_hash_table (info)->elf.splt)
 #define frvfdpic_pltrel_section(info) \
-  (frvfdpic_hash_table (info)->spltrel)
+  (frvfdpic_hash_table (info)->elf.srelplt)
 #define frvfdpic_relocs_info(info) \
   (frvfdpic_hash_table (info)->relocs_info)
 #define frvfdpic_got_initial_offset(info) \
   (frvfdpic_hash_table (info)->got0)
 #define frvfdpic_plt_initial_offset(info) \
   (frvfdpic_hash_table (info)->plt0)
+#define frvfdpic_dynamic_got_plt_info(info) \
+  (frvfdpic_hash_table (info)->g)
+
+/* Currently it's the same, but if some day we have a reason to change
+   it, we'd better be using a different macro.
+
+   FIXME: if there's any TLS PLT entry that uses local-exec or
+   initial-exec models, we could use the ret at the end of any of them
+   instead of adding one more.  */
+#define frvfdpic_plt_tls_ret_offset(info) \
+  (frvfdpic_plt_initial_offset (info))
+
+/* The name of the dynamic interpreter.  This is put in the .interp
+   section.  */
+
+#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1"
+
+#define DEFAULT_STACK_SIZE 0x20000
+
+/* This structure is used to collect the number of entries present in
+   each addressable range of the got.  */
+struct _frvfdpic_dynamic_got_info
+{
+  /* Several bits of information about the current link.  */
+  struct bfd_link_info *info;
+  /* Total GOT size needed for GOT entries within the 12-, 16- or 32-bit
+     ranges.  */
+  bfd_vma got12, gotlos, gothilo;
+  /* Total GOT size needed for function descriptor entries within the 12-,
+     16- or 32-bit ranges.  */
+  bfd_vma fd12, fdlos, fdhilo;
+  /* Total GOT size needed by function descriptor entries referenced
+     in PLT entries, that would be profitable to place in offsets
+     close to the PIC register.  */
+  bfd_vma fdplt;
+  /* Total PLT size needed by lazy PLT entries.  */
+  bfd_vma lzplt;
+  /* Total GOT size needed for TLS descriptor entries within the 12-,
+     16- or 32-bit ranges.  */
+  bfd_vma tlsd12, tlsdlos, tlsdhilo;
+  /* Total GOT size needed by TLS descriptors referenced in PLT
+     entries, that would be profitable to place in offers close to the
+     PIC register.  */
+  bfd_vma tlsdplt;
+  /* Total PLT size needed by TLS lazy PLT entries.  */
+  bfd_vma tlslzplt;
+  /* Number of relocations carried over from input object files.  */
+  unsigned long relocs;
+  /* Number of fixups introduced by relocations in input object files.  */
+  unsigned long fixups;
+  /* The number of fixups that reference the ret instruction added to
+     the PLT for locally-resolved TLS descriptors.  */
+  unsigned long tls_ret_refs;
+};
+
+/* This structure is used to assign offsets to got entries, function
+   descriptors, plt entries and lazy plt entries.  */
+
+struct _frvfdpic_dynamic_got_plt_info
+{
+  /* Summary information collected with _frvfdpic_count_got_plt_entries.  */
+  struct _frvfdpic_dynamic_got_info g;
+
+  /* For each addressable range, we record a MAX (positive) and MIN
+     (negative) value.  CUR is used to assign got entries, and it's
+     incremented from an initial positive value to MAX, then from MIN
+     to FDCUR (unless FDCUR wraps around first).  FDCUR is used to
+     assign function descriptors, and it's decreased from an initial
+     non-positive value to MIN, then from MAX down to CUR (unless CUR
+     wraps around first).  All of MIN, MAX, CUR and FDCUR always point
+     to even words.  ODD, if non-zero, indicates an odd word to be
+     used for the next got entry, otherwise CUR is used and
+     incremented by a pair of words, wrapping around when it reaches
+     MAX.  FDCUR is decremented (and wrapped) before the next function
+     descriptor is chosen.  FDPLT indicates the number of remaining
+     slots that can be used for function descriptors used only by PLT
+     entries.
+
+     TMAX, TMIN and TCUR are used to assign TLS descriptors.  TCUR
+     starts as MAX, and grows up to TMAX, then wraps around to TMIN
+     and grows up to MIN.  TLSDPLT indicates the number of remaining
+     slots that can be used for TLS descriptors used only by TLS PLT
+     entries.  */
+  struct _frvfdpic_dynamic_got_alloc_data
+  {
+    bfd_signed_vma max, cur, odd, fdcur, min;
+    bfd_signed_vma tmax, tcur, tmin;
+    bfd_vma fdplt, tlsdplt;
+  } got12, gotlos, gothilo;
+};
 
 /* Create an FRV ELF linker hash table.  */
 
@@ -640,14 +942,16 @@ static struct bfd_link_hash_table *
 frvfdpic_elf_link_hash_table_create (bfd *abfd)
 {
   struct frvfdpic_elf_link_hash_table *ret;
-  bfd_size_type amt = sizeof (struct frvfdpic_elf_link_hash_table);
+  size_t amt = sizeof (struct frvfdpic_elf_link_hash_table);
 
-  ret = bfd_zalloc (abfd, amt);
+  ret = bfd_zmalloc (amt);
   if (ret == NULL)
     return NULL;
 
-  if (! _bfd_elf_link_hash_table_init (&ret->elf, abfd,
-                                      _bfd_elf_link_hash_newfunc))
+  if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
+                                     _bfd_elf_link_hash_newfunc,
+                                     sizeof (struct elf_link_hash_entry),
+                                     FRV_ELF_DATA))
     {
       free (ret);
       return NULL;
@@ -707,6 +1011,24 @@ struct frvfdpic_relocs_info
   unsigned fdgoff12:1;
   unsigned fdgofflos:1;
   unsigned fdgoffhilo:1;
+  /* Whether a GETTLSOFF relocation references symbol+addend.  */
+  unsigned tlsplt:1;
+  /* FIXME: we should probably add tlspltdesc, tlspltoff and
+     tlspltimm, to tell what kind of TLS PLT entry we're generating.
+     We might instead just pre-compute flags telling whether the
+     object is suitable for local exec, initial exec or general
+     dynamic addressing, and use that all over the place.  We could
+     also try to do a better job of merging TLSOFF and TLSDESC entries
+     in main executables, but perhaps we can get rid of TLSDESC
+     entirely in them instead.  */
+  /* Whether a GOTTLSDESC relocation references symbol+addend.  */
+  unsigned tlsdesc12:1;
+  unsigned tlsdesclos:1;
+  unsigned tlsdeschilo:1;
+  /* Whether a GOTTLSOFF relocation references symbol+addend.  */
+  unsigned tlsoff12:1;
+  unsigned tlsofflos:1;
+  unsigned tlsoffhilo:1;
   /* Whether symbol+addend is referenced with GOTOFF12, GOTOFFLO or
      GOTOFFHI relocations.  The addend doesn't really matter, since we
      envision that this will only be used to check whether the symbol
@@ -725,7 +1047,7 @@ struct frvfdpic_relocs_info
      for symbol+addend.  Should be implied by something like:
      (plt || fdgotoff12 || fdgotofflos || fdgotofflohi
       || ((fd || fdgot12 || fdgotlos || fdgothilo)
-          && (symndx != -1 || FRVFDPIC_FUNCDESC_LOCAL (info, d.h))))  */
+         && (symndx != -1 || FRVFDPIC_FUNCDESC_LOCAL (info, d.h))))  */
   unsigned privfd:1;
   /* Whether a lazy PLT entry is needed for this symbol+addend.
      Should be implied by something like:
@@ -736,9 +1058,10 @@ struct frvfdpic_relocs_info
      needed for this symbol.  */
   unsigned done:1;
 
-  /* The number of R_FRV_32, R_FRV_FUNCDESC and R_FRV_FUNCDESC_VALUE
-     relocations referencing the symbol.  */
-  unsigned relocs32, relocsfd, relocsfdv;
+  /* The number of R_FRV_32, R_FRV_FUNCDESC, R_FRV_FUNCDESC_VALUE and
+     R_FRV_TLSDESC_VALUE, R_FRV_TLSOFF relocations referencing
+     symbol+addend.  */
+  unsigned relocs32, relocsfd, relocsfdv, relocstlsd, relocstlsoff;
 
   /* The number of .rofixups entries and dynamic relocations allocated
      for this symbol, minus any that might have already been used.  */
@@ -754,6 +1077,10 @@ struct frvfdpic_relocs_info
      non-lazy and lazy, respectively.  If unassigned, should be
      (bfd_vma)-1.  */
   bfd_vma plt_entry, lzplt_entry;
+  /* The offsets of the GOT entries for TLS offset and TLS descriptor.  */
+  bfd_signed_vma tlsoff_entry, tlsdesc_entry;
+  /* The offset of the TLS offset PLT entry.  */
+  bfd_vma tlsplt_entry;
 };
 
 /* Compute a hash with the key fields of an frvfdpic_relocs_info entry.  */
@@ -763,8 +1090,8 @@ frvfdpic_relocs_info_hash (const void *entry_)
   const struct frvfdpic_relocs_info *entry = entry_;
 
   return (entry->symndx == -1
-         ? entry->d.h->root.root.hash
-         : entry->symndx + entry->d.abfd->id * 257) + entry->addend;
+         ? (long) entry->d.h->root.root.hash
+         : entry->symndx + (long) entry->d.abfd->id * 257) + entry->addend;
 }
 
 /* Test whether the key fields of two frvfdpic_relocs_info entries are
@@ -807,6 +1134,7 @@ frvfdpic_relocs_info_find (struct htab *ht,
   (*loc)->addend = entry->addend;
   (*loc)->plt_entry = (bfd_vma)-1;
   (*loc)->lzplt_entry = (bfd_vma)-1;
+  (*loc)->tlsplt_entry = (bfd_vma)-1;
 
   return *loc;
 }
@@ -832,7 +1160,7 @@ frvfdpic_relocs_info_for_global (struct htab *ht,
 
 /* Obtain the address of the entry in HT associated with the SYMNDXth
    local symbol of the input bfd ABFD, plus the addend, creating a new
-   entry if none existed.  */  
+   entry if none existed.  */
 inline static struct frvfdpic_relocs_info *
 frvfdpic_relocs_info_for_local (struct htab *ht,
                                bfd *abfd,
@@ -866,39 +1194,16 @@ frvfdpic_pic_merge_early_relocs_info (struct frvfdpic_relocs_info *e2,
   e2->fdgoff12 |= e1->fdgoff12;
   e2->fdgofflos |= e1->fdgofflos;
   e2->fdgoffhilo |= e1->fdgoffhilo;
+  e2->tlsplt |= e1->tlsplt;
+  e2->tlsdesc12 |= e1->tlsdesc12;
+  e2->tlsdesclos |= e1->tlsdesclos;
+  e2->tlsdeschilo |= e1->tlsdeschilo;
+  e2->tlsoff12 |= e1->tlsoff12;
+  e2->tlsofflos |= e1->tlsofflos;
+  e2->tlsoffhilo |= e1->tlsoffhilo;
   e2->gotoff |= e1->gotoff;
   e2->call |= e1->call;
   e2->sym |= e1->sym;
-
-#if 0
-  /* These are set in _frvfdpic_count_got_plt_entries() or later, and this
-     function is only called in _frvfdpic_resolve_final_relocs_info(), that
-     runs just before it, so we don't have to worry about the fields
-     below.  */
-
-  e2->plt |= e1->plt;
-  e2->privfd |= e1->privfd;
-  e2->lazyplt |= e1->lazyplt;
-  e2->done |= e1->done;
-
-  e2->relocs32 += e1->relocs32;
-  e2->relocsfd += e1->relocsfd;
-  e2->relocsfdv += e1->relocsfdv;
-  e2->fixups += e1->fixups;
-  e2->dynrelocs += e1->dynrelocs;
-
-  if (abs (e1->got_entry) < abs (e2->got_entry))
-    e2->got_entry = e1->got_entry;
-  if (abs (e1->fdgot_entry) < abs (e2->fdgot_entry))
-    e2->fdgot_entry = e1->fdgot_entry;
-  if (abs (e1->fd_entry) < abs (e2->fd_entry))
-    e2->fd_entry = e1->fd_entry;
-
-  if (e1->plt_entry < e2->plt_entry)
-    e2->plt_entry = e1->plt_entry;
-  if (e1->lzplt_entry < e2->lzplt_entry)
-    e2->lzplt_entry = e1->lzplt_entry;
-#endif
 }
 
 /* Every block of 65535 lazy PLT entries shares a single call to the
@@ -981,26 +1286,9 @@ _frvfdpic_add_rofixup (bfd *output_bfd, asection *rofixup, bfd_vma offset,
 static unsigned
 _frvfdpic_osec_to_segment (bfd *output_bfd, asection *osec)
 {
-  struct elf_segment_map *m;
-  Elf_Internal_Phdr *p;
-
-  /* Find the segment that contains the output_section.  */
-  for (m = elf_tdata (output_bfd)->segment_map,
-        p = elf_tdata (output_bfd)->phdr;
-       m != NULL;
-       m = m->next, p++)
-    {
-      int i;
-
-      for (i = m->count - 1; i >= 0; i--)
-       if (m->sections[i] == osec)
-         break;
+  Elf_Internal_Phdr *p = _bfd_elf_find_segment_containing_section (output_bfd, osec);
 
-      if (i >= 0)
-       break;
-    }
-
-  return p - elf_tdata (output_bfd)->phdr;
+  return (p != NULL) ? p - elf_tdata (output_bfd)->phdr : -1;
 }
 
 inline static bfd_boolean
@@ -1011,6 +1299,21 @@ _frvfdpic_osec_readonly_p (bfd *output_bfd, asection *osec)
   return ! (elf_tdata (output_bfd)->phdr[seg].p_flags & PF_W);
 }
 
+#define FRVFDPIC_TLS_BIAS (2048 - 16)
+
+/* Return the base VMA address which should be subtracted from real addresses
+   when resolving TLSMOFF relocation.
+   This is PT_TLS segment p_vaddr, plus the 2048-16 bias.  */
+
+static bfd_vma
+tls_biased_base (struct bfd_link_info *info)
+{
+  /* If tls_sec is NULL, we should have signalled an error already.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return FRVFDPIC_TLS_BIAS;
+  return elf_hash_table (info)->tls_sec->vma + FRVFDPIC_TLS_BIAS;
+}
+
 /* Generate relocations for GOT entries, function descriptors, and
    code for PLT and lazy PLT entries.  */
 
@@ -1021,7 +1324,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
                                       asection *sec,
                                       Elf_Internal_Sym *sym,
                                       bfd_vma addend)
-                                 
+
 {
   bfd_vma fd_lazy_rel_offset = (bfd_vma)-1;
   int dynindx = -1;
@@ -1030,7 +1333,8 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
     return TRUE;
   entry->done = 1;
 
-  if (entry->got_entry || entry->fdgot_entry || entry->fd_entry)
+  if (entry->got_entry || entry->fdgot_entry || entry->fd_entry
+      || entry->tlsoff_entry || entry->tlsdesc_entry)
     {
       /* If the symbol is dynamic, consider it for dynamic
         relocations, otherwise decay to section + offset.  */
@@ -1038,7 +1342,8 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
        dynindx = entry->d.h->dynindx;
       else
        {
-         if (sec->output_section
+         if (sec
+             && sec->output_section
              && ! bfd_is_abs_section (sec->output_section)
              && ! bfd_is_und_section (sec->output_section))
            dynindx = elf_section_data (sec->output_section)->dynindx;
@@ -1072,7 +1377,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
       /* If we're linking an executable at a fixed address, we can
         omit the dynamic relocation as long as the symbol is local to
         this module.  */
-      if (info->executable && !info->pie
+      if (bfd_link_pde (info)
          && (entry->symndx != -1
              || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
        {
@@ -1099,7 +1404,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
                                 ->output_section->vma
                                 + frvfdpic_got_section (info)->output_offset,
                                 R_FRV_32, idx, ad, entry);
-       
+
       bfd_put_32 (output_bfd, ad,
                  frvfdpic_got_section (info)->contents
                  + frvfdpic_got_initial_offset (info)
@@ -1112,7 +1417,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
     {
       int reloc, idx;
       bfd_vma ad = 0;
-      
+
       if (! (entry->symndx == -1
             && entry->d.h->root.type == bfd_link_hash_undefweak
             && FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
@@ -1127,7 +1432,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
          if (entry->symndx == -1
              && ! FRVFDPIC_FUNCDESC_LOCAL (info, entry->d.h)
              && FRVFDPIC_SYM_LOCAL (info, entry->d.h)
-             && !(info->executable && !info->pie))
+             && !bfd_link_pde (info))
            {
              reloc = R_FRV_FUNCDESC;
              idx = elf_section_data (entry->d.h->root.u.def.section
@@ -1142,7 +1447,14 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
              idx = dynindx;
              ad = addend;
              if (ad)
-               return FALSE;
+               {
+                 (*info->callbacks->reloc_dangerous)
+                   (info, _("relocation requires zero addend"),
+                    elf_hash_table (info)->dynobj,
+                    frvfdpic_got_section (info),
+                    entry->fdgot_entry);
+                 return FALSE;
+               }
            }
          else
            {
@@ -1163,7 +1475,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
             dynamic symbol entry for the got section, so idx will be
             zero, which means we can and should compute the address
             of the private descriptor ourselves.  */
-         if (info->executable && !info->pie
+         if (bfd_link_pde (info)
              && (entry->symndx != -1
                  || FRVFDPIC_FUNCDESC_LOCAL (info, entry->d.h)))
            {
@@ -1226,7 +1538,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
       /* If we're linking an executable at a fixed address, we can
         omit the dynamic relocation as long as the symbol is local to
         this module.  */
-      if (info->executable && !info->pie
+      if (bfd_link_pde (info)
          && (entry->symndx != -1 || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
        {
          if (sec)
@@ -1274,7 +1586,9 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
 
       /* If we've omitted the dynamic relocation, just emit the fixed
         addresses of the symbol and of the local GOT base offset.  */
-      if (info->executable && !info->pie && sec && sec->output_section)
+      if (bfd_link_pde (info)
+         && sec
+         && sec->output_section)
        {
          lowword = ad;
          highword = frvfdpic_got_section (info)->output_section->vma
@@ -1284,8 +1598,15 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
       else if (entry->lazyplt)
        {
          if (ad)
-           return FALSE;
-         
+           {
+             (*info->callbacks->reloc_dangerous)
+               (info, _("relocation requires zero addend"),
+                elf_hash_table (info)->dynobj,
+                frvfdpic_got_section (info),
+                entry->fd_entry);
+             return FALSE;
+           }
+
          fd_lazy_rel_offset = ofst;
 
          /* A function descriptor used for lazy or local resolving is
@@ -1297,7 +1618,7 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
          lowword = entry->lzplt_entry + 4
            + frvfdpic_plt_section (info)->output_offset
            + frvfdpic_plt_section (info)->output_section->vma;
-         highword = _frvfdpic_osec_to_segment 
+         highword = _frvfdpic_osec_to_segment
            (output_bfd, frvfdpic_plt_section (info)->output_section);
        }
       else
@@ -1306,8 +1627,9 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
             of the section.  For a non-local function, it's
             disregarded.  */
          lowword = ad;
-         if (entry->symndx == -1 && entry->d.h->dynindx != -1
-             && entry->d.h->dynindx == idx)
+         if (sec == NULL
+             || (entry->symndx == -1 && entry->d.h->dynindx != -1
+                 && entry->d.h->dynindx == idx))
            highword = 0;
          else
            highword = _frvfdpic_osec_to_segment
@@ -1364,11 +1686,12 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
                          | ((entry->fd_entry >> 16)
                             & (((bfd_vma)1 << 16) - 1)),
                          plt_code);
+             plt_code += 4;
              bfd_put_32 (output_bfd,
                          0x9cf40000
                          | (entry->fd_entry & (((bfd_vma)1 << 16) - 1)),
                          plt_code);
-             plt_code += 8;
+             plt_code += 4;
            }
          /* ldd @(gr14,gr15),gr14 */
          bfd_put_32 (output_bfd, 0x9c08e14f, plt_code);
@@ -1412,20 +1735,398 @@ _frvfdpic_emit_got_relocs_plt_entries (struct frvfdpic_relocs_info *entry,
        }
     }
 
+  /* Generate relocation for GOT entry holding the TLS offset.  */
+  if (entry->tlsoff_entry)
+    {
+      int idx = dynindx;
+      bfd_vma ad = addend;
+
+      if (entry->symndx != -1
+         || FRVFDPIC_SYM_LOCAL (info, entry->d.h))
+       {
+         /* If the symbol is dynamic but binds locally, use
+            section+offset.  */
+         if (sec)
+           {
+             if (entry->symndx == -1)
+               ad += entry->d.h->root.u.def.value;
+             else
+               ad += sym->st_value;
+             ad += sec->output_offset;
+             if (sec->output_section
+                 && elf_section_data (sec->output_section))
+               idx = elf_section_data (sec->output_section)->dynindx;
+             else
+               idx = 0;
+           }
+       }
+
+      /* *ABS*+addend is special for TLS relocations, use only the
+        addend.  */
+      if (bfd_link_executable (info)
+         && idx == 0
+         && (bfd_is_abs_section (sec)
+             || bfd_is_und_section (sec)))
+       ;
+      /* If we're linking an executable, we can entirely omit the
+        dynamic relocation if the symbol is local to this module.  */
+      else if (bfd_link_executable (info)
+              && (entry->symndx != -1
+                  || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         if (sec)
+           ad += sec->output_section->vma - tls_biased_base (info);
+       }
+      else
+       {
+         if (idx == 0
+             && (bfd_is_abs_section (sec)
+                 || bfd_is_und_section (sec)))
+           {
+             if (! elf_hash_table (info)->tls_sec)
+               {
+                 (*info->callbacks->undefined_symbol)
+                   (info, "TLS section", elf_hash_table (info)->dynobj,
+                    frvfdpic_got_section (info), entry->tlsoff_entry, TRUE);
+                 return FALSE;
+               }
+             idx = elf_section_data (elf_hash_table (info)->tls_sec)->dynindx;
+             ad += FRVFDPIC_TLS_BIAS;
+           }
+         _frvfdpic_add_dyn_reloc (output_bfd, frvfdpic_gotrel_section (info),
+                                  _bfd_elf_section_offset
+                                  (output_bfd, info,
+                                   frvfdpic_got_section (info),
+                                   frvfdpic_got_initial_offset (info)
+                                   + entry->tlsoff_entry)
+                                  + frvfdpic_got_section (info)
+                                  ->output_section->vma
+                                  + frvfdpic_got_section (info)
+                                  ->output_offset,
+                                  R_FRV_TLSOFF, idx, ad, entry);
+       }
+
+      bfd_put_32 (output_bfd, ad,
+                 frvfdpic_got_section (info)->contents
+                 + frvfdpic_got_initial_offset (info)
+                 + entry->tlsoff_entry);
+    }
+
+  if (entry->tlsdesc_entry)
+    {
+      int idx = dynindx;
+      bfd_vma ad = addend;
+
+      /* If the symbol is dynamic but binds locally, use
+        section+offset.  */
+      if (sec && (entry->symndx != -1
+                 || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         if (entry->symndx == -1)
+           ad += entry->d.h->root.u.def.value;
+         else
+           ad += sym->st_value;
+         ad += sec->output_offset;
+         if (sec->output_section && elf_section_data (sec->output_section))
+           idx = elf_section_data (sec->output_section)->dynindx;
+         else
+           idx = 0;
+       }
+
+      /* If we didn't set up a TLS offset entry, but we're linking an
+        executable and the symbol binds locally, we can use the
+        module offset in the TLS descriptor in relaxations.  */
+      if (bfd_link_executable (info) && ! entry->tlsoff_entry)
+       entry->tlsoff_entry = entry->tlsdesc_entry + 4;
+
+      if (bfd_link_pde (info)
+         && ((idx == 0
+              && (bfd_is_abs_section (sec)
+                  || bfd_is_und_section (sec)))
+             || entry->symndx != -1
+             || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         /* *ABS*+addend is special for TLS relocations, use only the
+            addend for the TLS offset, and take the module id as
+            0.  */
+         if (idx == 0
+             && (bfd_is_abs_section (sec)
+                 || bfd_is_und_section (sec)))
+           ;
+         /* For other TLS symbols that bind locally, add the section
+            TLS offset to the addend.  */
+         else if (sec)
+           ad += sec->output_section->vma - tls_biased_base (info);
+
+         bfd_put_32 (output_bfd,
+                     frvfdpic_plt_section (info)->output_section->vma
+                     + frvfdpic_plt_section (info)->output_offset
+                     + frvfdpic_plt_tls_ret_offset (info),
+                     frvfdpic_got_section (info)->contents
+                     + frvfdpic_got_initial_offset (info)
+                     + entry->tlsdesc_entry);
+
+         _frvfdpic_add_rofixup (output_bfd,
+                                frvfdpic_gotfixup_section (info),
+                                frvfdpic_got_section (info)
+                                ->output_section->vma
+                                + frvfdpic_got_section (info)
+                                ->output_offset
+                                + frvfdpic_got_initial_offset (info)
+                                + entry->tlsdesc_entry, entry);
+
+         BFD_ASSERT (frvfdpic_dynamic_got_plt_info (info)->tls_ret_refs);
+
+         /* We've used one of the reserved fixups, so discount it so
+            that we can check at the end that we've used them
+            all.  */
+         frvfdpic_dynamic_got_plt_info (info)->tls_ret_refs--;
+
+         /* While at that, make sure the ret instruction makes to the
+            right location in the PLT.  We could do it only when we
+            got to 0, but since the check at the end will only print
+            a warning, make sure we have the ret in place in case the
+            warning is missed.  */
+         bfd_put_32 (output_bfd, 0xc03a4000,
+                     frvfdpic_plt_section (info)->contents
+                     + frvfdpic_plt_tls_ret_offset (info));
+       }
+      else
+       {
+         if (idx == 0
+             && (bfd_is_abs_section (sec)
+                 || bfd_is_und_section (sec)))
+           {
+             if (! elf_hash_table (info)->tls_sec)
+               {
+                 (*info->callbacks->undefined_symbol)
+                   (info, "TLS section", elf_hash_table (info)->dynobj,
+                    frvfdpic_got_section (info), entry->tlsdesc_entry, TRUE);
+                 return FALSE;
+               }
+             idx = elf_section_data (elf_hash_table (info)->tls_sec)->dynindx;
+             ad += FRVFDPIC_TLS_BIAS;
+           }
+
+         _frvfdpic_add_dyn_reloc (output_bfd, frvfdpic_gotrel_section (info),
+                                  _bfd_elf_section_offset
+                                  (output_bfd, info,
+                                   frvfdpic_got_section (info),
+                                   frvfdpic_got_initial_offset (info)
+                                   + entry->tlsdesc_entry)
+                                  + frvfdpic_got_section (info)
+                                  ->output_section->vma
+                                  + frvfdpic_got_section (info)
+                                  ->output_offset,
+                                  R_FRV_TLSDESC_VALUE, idx, ad, entry);
+
+         bfd_put_32 (output_bfd, 0,
+                     frvfdpic_got_section (info)->contents
+                     + frvfdpic_got_initial_offset (info)
+                     + entry->tlsdesc_entry);
+       }
+
+      bfd_put_32 (output_bfd, ad,
+                 frvfdpic_got_section (info)->contents
+                 + frvfdpic_got_initial_offset (info)
+                 + entry->tlsdesc_entry + 4);
+    }
+
+  /* Generate code for the get-TLS-offset PLT entry.  */
+  if (entry->tlsplt_entry != (bfd_vma) -1)
+    {
+      bfd_byte *plt_code = frvfdpic_plt_section (info)->contents
+       + entry->tlsplt_entry;
+
+      if (bfd_link_executable (info)
+         && (entry->symndx != -1
+             || FRVFDPIC_SYM_LOCAL (info, entry->d.h)))
+       {
+         int idx = dynindx;
+         bfd_vma ad = addend;
+
+         /* sec may be NULL when referencing an undefweak symbol
+            while linking a static executable.  */
+         if (!sec)
+           {
+             BFD_ASSERT (entry->symndx == -1
+                         && entry->d.h->root.type == bfd_link_hash_undefweak);
+           }
+         else
+           {
+             if (entry->symndx == -1)
+               ad += entry->d.h->root.u.def.value;
+             else
+               ad += sym->st_value;
+             ad += sec->output_offset;
+             if (sec->output_section
+                 && elf_section_data (sec->output_section))
+               idx = elf_section_data (sec->output_section)->dynindx;
+             else
+               idx = 0;
+           }
+
+         /* *ABS*+addend is special for TLS relocations, use only the
+            addend for the TLS offset, and take the module id as
+            0.  */
+         if (idx == 0
+             && (bfd_is_abs_section (sec)
+                 || bfd_is_und_section (sec)))
+           ;
+         /* For other TLS symbols that bind locally, add the section
+            TLS offset to the addend.  */
+         else if (sec)
+           ad += sec->output_section->vma - tls_biased_base (info);
+
+         if ((bfd_signed_vma)ad >= -(1 << (16 - 1))
+             && (bfd_signed_vma)ad < (1 << (16 - 1)))
+           {
+             /* setlos lo(ad), gr9 */
+             bfd_put_32 (output_bfd,
+                         0x92fc0000
+                         | (ad
+                            & (((bfd_vma)1 << 16) - 1)),
+                         plt_code);
+             plt_code += 4;
+           }
+         else
+           {
+             /* sethi.p hi(ad), gr9
+                setlo lo(ad), gr9 */
+             bfd_put_32 (output_bfd,
+                         0x12f80000
+                         | ((ad >> 16)
+                            & (((bfd_vma)1 << 16) - 1)),
+                         plt_code);
+             plt_code += 4;
+             bfd_put_32 (output_bfd,
+                         0x92f40000
+                         | (ad
+                            & (((bfd_vma)1 << 16) - 1)),
+                         plt_code);
+             plt_code += 4;
+           }
+         /* ret */
+         bfd_put_32 (output_bfd, 0xc03a4000, plt_code);
+       }
+      else if (entry->tlsoff_entry)
+       {
+         /* Figure out what kind of PLT entry we need, depending on the
+            location of the TLS descriptor within the GOT.  */
+         if (entry->tlsoff_entry >= -(1 << (12 - 1))
+             && entry->tlsoff_entry < (1 << (12 - 1)))
+           {
+             /* ldi @(gr15, tlsoff_entry), gr9 */
+             bfd_put_32 (output_bfd,
+                         0x92c8f000 | (entry->tlsoff_entry
+                                       & ((1 << 12) - 1)),
+                         plt_code);
+             plt_code += 4;
+           }
+         else
+           {
+             if (entry->tlsoff_entry >= -(1 << (16 - 1))
+                 && entry->tlsoff_entry < (1 << (16 - 1)))
+               {
+                 /* setlos lo(tlsoff_entry), gr8 */
+                 bfd_put_32 (output_bfd,
+                             0x90fc0000
+                             | (entry->tlsoff_entry
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+               }
+             else
+               {
+                 /* sethi.p hi(tlsoff_entry), gr8
+                    setlo lo(tlsoff_entry), gr8 */
+                 bfd_put_32 (output_bfd,
+                             0x10f80000
+                             | ((entry->tlsoff_entry >> 16)
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+                 bfd_put_32 (output_bfd,
+                             0x90f40000
+                             | (entry->tlsoff_entry
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+               }
+             /* ld @(gr15,gr8),gr9 */
+             bfd_put_32 (output_bfd, 0x9008f108, plt_code);
+             plt_code += 4;
+           }
+         /* ret */
+         bfd_put_32 (output_bfd, 0xc03a4000, plt_code);
+       }
+      else
+       {
+         BFD_ASSERT (entry->tlsdesc_entry);
+
+         /* Figure out what kind of PLT entry we need, depending on the
+            location of the TLS descriptor within the GOT.  */
+         if (entry->tlsdesc_entry >= -(1 << (12 - 1))
+             && entry->tlsdesc_entry < (1 << (12 - 1)))
+           {
+             /* lddi @(gr15, tlsdesc_entry), gr8 */
+             bfd_put_32 (output_bfd,
+                         0x90ccf000 | (entry->tlsdesc_entry
+                                       & ((1 << 12) - 1)),
+                         plt_code);
+             plt_code += 4;
+           }
+         else
+           {
+             if (entry->tlsdesc_entry >= -(1 << (16 - 1))
+                 && entry->tlsdesc_entry < (1 << (16 - 1)))
+               {
+                 /* setlos lo(tlsdesc_entry), gr8 */
+                 bfd_put_32 (output_bfd,
+                             0x90fc0000
+                             | (entry->tlsdesc_entry
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+               }
+             else
+               {
+                 /* sethi.p hi(tlsdesc_entry), gr8
+                    setlo lo(tlsdesc_entry), gr8 */
+                 bfd_put_32 (output_bfd,
+                             0x10f80000
+                             | ((entry->tlsdesc_entry >> 16)
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+                 bfd_put_32 (output_bfd,
+                             0x90f40000
+                             | (entry->tlsdesc_entry
+                                & (((bfd_vma)1 << 16) - 1)),
+                             plt_code);
+                 plt_code += 4;
+               }
+             /* ldd @(gr15,gr8),gr8 */
+             bfd_put_32 (output_bfd, 0x9008f148, plt_code);
+             plt_code += 4;
+           }
+         /* jmpl @(gr8,gr0) */
+         bfd_put_32 (output_bfd, 0x80308000, plt_code);
+       }
+    }
+
   return TRUE;
 }
 
 /* Handle an FRV small data reloc.  */
 
 static bfd_reloc_status_type
-elf32_frv_relocate_gprel12 (info, input_bfd, input_section, relocation,
-                           contents, value)
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     Elf_Internal_Rela *relocation;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_gprel12 (struct bfd_link_info *info,
+                           bfd *input_bfd,
+                           asection *input_section,
+                           Elf_Internal_Rela *relocation,
+                           bfd_byte *contents,
+                           bfd_vma value)
 {
   bfd_vma insn;
   bfd_vma gp;
@@ -1457,14 +2158,12 @@ elf32_frv_relocate_gprel12 (info, input_bfd, input_section, relocation,
 /* Handle an FRV small data reloc. for the u12 field.  */
 
 static bfd_reloc_status_type
-elf32_frv_relocate_gprelu12 (info, input_bfd, input_section, relocation,
-                            contents, value)
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     Elf_Internal_Rela *relocation;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_gprelu12 (struct bfd_link_info *info,
+                            bfd *input_bfd,
+                            asection *input_section,
+                            Elf_Internal_Rela *relocation,
+                            bfd_byte *contents,
+                            bfd_vma value)
 {
   bfd_vma insn;
   bfd_vma gp;
@@ -1499,11 +2198,10 @@ elf32_frv_relocate_gprelu12 (info, input_bfd, input_section, relocation,
 /* Handle an FRV ELF HI16 reloc.  */
 
 static bfd_reloc_status_type
-elf32_frv_relocate_hi16 (input_bfd, relhi, contents, value)
-     bfd *input_bfd;
-     Elf_Internal_Rela *relhi;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_hi16 (bfd *input_bfd,
+                        Elf_Internal_Rela *relhi,
+                        bfd_byte *contents,
+                        bfd_vma value)
 {
   bfd_vma insn;
 
@@ -1522,11 +2220,10 @@ elf32_frv_relocate_hi16 (input_bfd, relhi, contents, value)
 
 }
 static bfd_reloc_status_type
-elf32_frv_relocate_lo16 (input_bfd, rello, contents, value)
-     bfd *input_bfd;
-     Elf_Internal_Rela *rello;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_lo16 (bfd *input_bfd,
+                        Elf_Internal_Rela *rello,
+                        bfd_byte *contents,
+                        bfd_vma value)
 {
   bfd_vma insn;
 
@@ -1547,12 +2244,11 @@ elf32_frv_relocate_lo16 (input_bfd, rello, contents, value)
 /* Perform the relocation for the CALL label24 instruction.  */
 
 static bfd_reloc_status_type
-elf32_frv_relocate_label24 (input_bfd, input_section, rello, contents, value)
-     bfd *input_bfd;
-     asection *input_section;
-     Elf_Internal_Rela *rello;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_label24 (bfd *input_bfd,
+                           asection *input_section,
+                           Elf_Internal_Rela *rello,
+                           bfd_byte *contents,
+                           bfd_vma value)
 {
   bfd_vma insn;
   bfd_vma label6;
@@ -1590,14 +2286,12 @@ elf32_frv_relocate_label24 (input_bfd, input_section, rello, contents, value)
 }
 
 static bfd_reloc_status_type
-elf32_frv_relocate_gprelhi (info, input_bfd, input_section, relocation,
-                           contents, value)
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     Elf_Internal_Rela *relocation;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_gprelhi (struct bfd_link_info *info,
+                           bfd *input_bfd,
+                           asection *input_section,
+                           Elf_Internal_Rela *relocation,
+                           bfd_byte *contents,
+                           bfd_vma value)
 {
   bfd_vma insn;
   bfd_vma gp;
@@ -1606,8 +2300,8 @@ elf32_frv_relocate_gprelhi (info, input_bfd, input_section, relocation,
   h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
 
   gp = (h->u.def.value
-        + h->u.def.section->output_section->vma
-        + h->u.def.section->output_offset);
+       + h->u.def.section->output_section->vma
+       + h->u.def.section->output_offset);
 
   value -= input_section->output_section->vma;
   value -= (gp - input_section->output_section->vma);
@@ -1625,14 +2319,12 @@ elf32_frv_relocate_gprelhi (info, input_bfd, input_section, relocation,
 }
 
 static bfd_reloc_status_type
-elf32_frv_relocate_gprello (info, input_bfd, input_section, relocation,
-                           contents, value)
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     Elf_Internal_Rela *relocation;
-     bfd_byte *contents;
-     bfd_vma value;
+elf32_frv_relocate_gprello (struct bfd_link_info *info,
+                           bfd *input_bfd,
+                           asection *input_section,
+                           Elf_Internal_Rela *relocation,
+                           bfd_byte *contents,
+                           bfd_vma value)
 {
   bfd_vma insn;
   bfd_vma gp;
@@ -1641,8 +2333,8 @@ elf32_frv_relocate_gprello (info, input_bfd, input_section, relocation,
   h = bfd_link_hash_lookup (info->hash, "_gp", FALSE, FALSE, TRUE);
 
   gp = (h->u.def.value
-        + h->u.def.section->output_section->vma
-        + h->u.def.section->output_offset);
+       + h->u.def.section->output_section->vma
+       + h->u.def.section->output_offset);
 
   value -= input_section->output_section->vma;
   value -= (gp - input_section->output_section->vma);
@@ -1661,9 +2353,8 @@ elf32_frv_relocate_gprello (info, input_bfd, input_section, relocation,
 }
 
 static reloc_howto_type *
-frv_reloc_type_lookup (abfd, code)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     bfd_reloc_code_real_type code;
+frv_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+                      bfd_reloc_code_real_type code)
 {
   switch (code)
     {
@@ -1756,6 +2447,60 @@ frv_reloc_type_lookup (abfd, code)
     case BFD_RELOC_FRV_GOTOFFLO:
       return &elf32_frv_howto_table[ (int) R_FRV_GOTOFFLO];
 
+    case BFD_RELOC_FRV_GETTLSOFF:
+      return &elf32_frv_howto_table[ (int) R_FRV_GETTLSOFF];
+
+    case BFD_RELOC_FRV_TLSDESC_VALUE:
+      if (elf_elfheader (abfd)->e_type == ET_EXEC
+         || elf_elfheader (abfd)->e_type == ET_DYN)
+       return &elf32_frv_rel_tlsdesc_value_howto;
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSDESC_VALUE];
+
+    case BFD_RELOC_FRV_GOTTLSDESC12:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESC12];
+
+    case BFD_RELOC_FRV_GOTTLSDESCHI:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESCHI];
+
+    case BFD_RELOC_FRV_GOTTLSDESCLO:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSDESCLO];
+
+    case BFD_RELOC_FRV_TLSMOFF12:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFF12];
+
+    case BFD_RELOC_FRV_TLSMOFFHI:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFFHI];
+
+    case BFD_RELOC_FRV_TLSMOFFLO:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFFLO];
+
+    case BFD_RELOC_FRV_GOTTLSOFF12:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFF12];
+
+    case BFD_RELOC_FRV_GOTTLSOFFHI:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFFHI];
+
+    case BFD_RELOC_FRV_GOTTLSOFFLO:
+      return &elf32_frv_howto_table[ (int) R_FRV_GOTTLSOFFLO];
+
+    case BFD_RELOC_FRV_TLSOFF:
+      if (elf_elfheader (abfd)->e_type == ET_EXEC
+         || elf_elfheader (abfd)->e_type == ET_DYN)
+       return &elf32_frv_rel_tlsoff_howto;
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSOFF];
+
+    case BFD_RELOC_FRV_TLSDESC_RELAX:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSDESC_RELAX];
+
+    case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+      return &elf32_frv_howto_table[ (int) R_FRV_GETTLSOFF_RELAX];
+
+    case BFD_RELOC_FRV_TLSOFF_RELAX:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSOFF_RELAX];
+
+    case BFD_RELOC_FRV_TLSMOFF:
+      return &elf32_frv_howto_table[ (int) R_FRV_TLSMOFF];
+
     case BFD_RELOC_VTABLE_INHERIT:
       return &elf32_frv_vtinherit_howto;
 
@@ -1766,13 +2511,32 @@ frv_reloc_type_lookup (abfd, code)
   return NULL;
 }
 
+static reloc_howto_type *
+frv_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name)
+{
+  unsigned int i;
+
+  for (i = 0;
+       i < sizeof (elf32_frv_howto_table) / sizeof (elf32_frv_howto_table[0]);
+       i++)
+    if (elf32_frv_howto_table[i].name != NULL
+       && strcasecmp (elf32_frv_howto_table[i].name, r_name) == 0)
+      return &elf32_frv_howto_table[i];
+
+  if (strcasecmp (elf32_frv_vtinherit_howto.name, r_name) == 0)
+    return &elf32_frv_vtinherit_howto;
+  if (strcasecmp (elf32_frv_vtentry_howto.name, r_name) == 0)
+    return &elf32_frv_vtentry_howto;
+
+  return NULL;
+}
+
 /* Set the howto pointer for an FRV ELF reloc.  */
 
-static void
-frv_info_to_howto_rela (abfd, cache_ptr, dst)
-     bfd *abfd ATTRIBUTE_UNUSED;
-     arelent *cache_ptr;
-     Elf_Internal_Rela *dst;
+static bfd_boolean
+frv_info_to_howto_rela (bfd *abfd ATTRIBUTE_UNUSED,
+                       arelent *cache_ptr,
+                       Elf_Internal_Rela *dst)
 {
   unsigned int r_type;
 
@@ -1788,13 +2552,23 @@ frv_info_to_howto_rela (abfd, cache_ptr, dst)
       break;
 
     default:
+      if (r_type >= ARRAY_SIZE (elf32_frv_howto_table))
+       {
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                             abfd, r_type);
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
       cache_ptr->howto = & elf32_frv_howto_table [r_type];
       break;
     }
+  return TRUE;
 }
 
 /* Set the howto pointer for an FRV ELF REL reloc.  */
-static void
+
+static bfd_boolean
 frvfdpic_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
                            arelent *cache_ptr, Elf_Internal_Rela *dst)
 {
@@ -1815,24 +2589,31 @@ frvfdpic_info_to_howto_rel (bfd *abfd ATTRIBUTE_UNUSED,
       cache_ptr->howto = &elf32_frv_rel_funcdesc_value_howto;
       break;
 
+    case R_FRV_TLSDESC_VALUE:
+      cache_ptr->howto = &elf32_frv_rel_tlsdesc_value_howto;
+      break;
+
+    case R_FRV_TLSOFF:
+      cache_ptr->howto = &elf32_frv_rel_tlsoff_howto;
+      break;
+
     default:
       cache_ptr->howto = NULL;
-      break;
+      return FALSE;
     }
+  return TRUE;
 }
 \f
 /* Perform a single relocation.  By default we use the standard BFD
    routines, but a few relocs, we have to do them ourselves.  */
 
 static bfd_reloc_status_type
-frv_final_link_relocate (howto, input_bfd, input_section, contents, rel,
-                        relocation)
-     reloc_howto_type *howto;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *rel;
-     bfd_vma relocation;
+frv_final_link_relocate (reloc_howto_type *howto,
+                        bfd *input_bfd,
+                        asection *input_section,
+                        bfd_byte *contents,
+                        Elf_Internal_Rela *rel,
+                        bfd_vma relocation)
 {
   return _bfd_final_link_relocate (howto, input_bfd, input_section,
                                   contents, rel->r_offset, relocation,
@@ -1871,27 +2652,23 @@ frv_final_link_relocate (howto, input_bfd, input_section, contents, rel,
    accordingly.  */
 
 static bfd_boolean
-elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
-                           contents, relocs, local_syms, local_sections)
-     bfd *output_bfd ATTRIBUTE_UNUSED;
-     struct bfd_link_info *info;
-     bfd *input_bfd;
-     asection *input_section;
-     bfd_byte *contents;
-     Elf_Internal_Rela *relocs;
-     Elf_Internal_Sym *local_syms;
-     asection **local_sections;
+elf32_frv_relocate_section (bfd *output_bfd ATTRIBUTE_UNUSED,
+                           struct bfd_link_info *info,
+                           bfd *input_bfd,
+                           asection *input_section,
+                           bfd_byte *contents,
+                           Elf_Internal_Rela *relocs,
+                           Elf_Internal_Sym *local_syms,
+                           asection **local_sections)
 {
   Elf_Internal_Shdr *symtab_hdr;
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
-  unsigned isec_segment, got_segment, plt_segment, gprel_segment,
+  unsigned isec_segment, got_segment, plt_segment, gprel_segment, tls_segment,
     check_segment[2];
-  int silence_segment_error = !(info->shared || info->pie);
-
-  if (info->relocatable)
-    return TRUE;
+  int silence_segment_error = !bfd_link_pic (info);
+  unsigned long insn;
 
   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (input_bfd);
@@ -1911,12 +2688,17 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
                                               ->output_section);
   else
     gprel_segment = -1;
-  if (IS_FDPIC (output_bfd) && elf_hash_table (info)->dynamic_sections_created)
+  if (IS_FDPIC (output_bfd) && frvfdpic_plt_section (info))
     plt_segment = _frvfdpic_osec_to_segment (output_bfd,
                                             frvfdpic_plt_section (info)
                                             ->output_section);
   else
     plt_segment = -1;
+  if (elf_hash_table (info)->tls_sec)
+    tls_segment = _frvfdpic_osec_to_segment (output_bfd,
+                                            elf_hash_table (info)->tls_sec);
+  else
+    tls_segment = -1;
 
   for (rel = relocs; rel < relend; rel ++)
     {
@@ -1927,10 +2709,10 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
       struct elf_link_hash_entry *h;
       bfd_vma relocation;
       bfd_reloc_status_type r;
-      const char * name = NULL;
+      const char *name;
       int r_type;
       asection *osec;
-      struct frvfdpic_relocs_info *picrel;
+      struct frvfdpic_relocs_info *picrel = NULL;
       bfd_vma orig_addend = rel->r_addend;
 
       r_type = ELF32_R_TYPE (rel->r_info);
@@ -1939,129 +2721,733 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
          || r_type == R_FRV_GNU_VTENTRY)
        continue;
 
-      /* This is a final link.  */
-      r_symndx = ELF32_R_SYM (rel->r_info);
-      howto  = elf32_frv_howto_table + ELF32_R_TYPE (rel->r_info);
-      h      = NULL;
-      sym    = NULL;
-      sec    = NULL;
+      r_symndx = ELF32_R_SYM (rel->r_info);
+      howto  = elf32_frv_howto_table + ELF32_R_TYPE (rel->r_info);
+      h      = NULL;
+      sym    = NULL;
+      sec    = NULL;
+
+      if (r_symndx < symtab_hdr->sh_info)
+       {
+         sym = local_syms + r_symndx;
+         osec = sec = local_sections [r_symndx];
+         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+
+         name = bfd_elf_string_from_elf_section
+           (input_bfd, symtab_hdr->sh_link, sym->st_name);
+         if (name == NULL || name[0] == 0)
+           name = bfd_section_name (sec);
+       }
+      else
+       {
+         bfd_boolean warned, ignored;
+         bfd_boolean unresolved_reloc;
+
+         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
+                                  r_symndx, symtab_hdr, sym_hashes,
+                                  h, sec, relocation,
+                                  unresolved_reloc, warned, ignored);
+         osec = sec;
+         name = h->root.root.string;
+       }
+
+      if (sec != NULL && discarded_section (sec))
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, 1, relend, howto, 0, contents);
+
+      if (bfd_link_relocatable (info))
+       continue;
+
+      if (r_type != R_FRV_TLSMOFF
+         && h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && !FRVFDPIC_SYM_LOCAL (info, h))
+       {
+         osec = sec = NULL;
+         relocation = 0;
+       }
+
+      switch (r_type)
+       {
+       case R_FRV_LABEL24:
+       case R_FRV_32:
+         if (! IS_FDPIC (output_bfd))
+           goto non_fdpic;
+         /* Fall through.  */
+
+       case R_FRV_GOT12:
+       case R_FRV_GOTHI:
+       case R_FRV_GOTLO:
+       case R_FRV_FUNCDESC_GOT12:
+       case R_FRV_FUNCDESC_GOTHI:
+       case R_FRV_FUNCDESC_GOTLO:
+       case R_FRV_GOTOFF12:
+       case R_FRV_GOTOFFHI:
+       case R_FRV_GOTOFFLO:
+       case R_FRV_FUNCDESC_GOTOFF12:
+       case R_FRV_FUNCDESC_GOTOFFHI:
+       case R_FRV_FUNCDESC_GOTOFFLO:
+       case R_FRV_FUNCDESC:
+       case R_FRV_FUNCDESC_VALUE:
+       case R_FRV_GETTLSOFF:
+       case R_FRV_TLSDESC_VALUE:
+       case R_FRV_GOTTLSDESC12:
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+       case R_FRV_TLSMOFF12:
+       case R_FRV_TLSMOFFHI:
+       case R_FRV_TLSMOFFLO:
+       case R_FRV_GOTTLSOFF12:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
+       case R_FRV_TLSOFF:
+       case R_FRV_TLSDESC_RELAX:
+       case R_FRV_GETTLSOFF_RELAX:
+       case R_FRV_TLSOFF_RELAX:
+       case R_FRV_TLSMOFF:
+         if ((input_section->flags & SEC_ALLOC) == 0)
+           break;
+
+         if (h != NULL)
+           picrel = frvfdpic_relocs_info_for_global (frvfdpic_relocs_info
+                                                     (info), input_bfd, h,
+                                                     orig_addend, INSERT);
+         else
+           /* In order to find the entry we created before, we must
+              use the original addend, not the one that may have been
+              modified by _bfd_elf_rela_local_sym().  */
+           picrel = frvfdpic_relocs_info_for_local (frvfdpic_relocs_info
+                                                    (info), input_bfd, r_symndx,
+                                                    orig_addend, INSERT);
+         if (! picrel)
+           return FALSE;
+
+         if (!_frvfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info,
+                                                     osec, sym,
+                                                     rel->r_addend))
+           {
+             info->callbacks->einfo
+               /* xgettext:c-format */
+               (_("%H: relocation to `%s+%v'"
+                  " may have caused the error above\n"),
+                input_bfd, input_section, rel->r_offset, name, rel->r_addend);
+             return FALSE;
+           }
+
+         break;
+
+       default:
+       non_fdpic:
+         picrel = NULL;
+         if (h
+             && ! FRVFDPIC_SYM_LOCAL (info, h)
+             && _bfd_elf_section_offset (output_bfd, info, input_section,
+                                         rel->r_offset) != (bfd_vma) -1)
+           {
+             info->callbacks->einfo
+               (_("%H: relocation references symbol"
+                  " not defined in the module\n"),
+                input_bfd, input_section, rel->r_offset);
+             return FALSE;
+           }
+         break;
+       }
+
+      switch (r_type)
+       {
+       case R_FRV_GETTLSOFF:
+       case R_FRV_TLSDESC_VALUE:
+       case R_FRV_GOTTLSDESC12:
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+       case R_FRV_TLSMOFF12:
+       case R_FRV_TLSMOFFHI:
+       case R_FRV_TLSMOFFLO:
+       case R_FRV_GOTTLSOFF12:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
+       case R_FRV_TLSOFF:
+       case R_FRV_TLSDESC_RELAX:
+       case R_FRV_GETTLSOFF_RELAX:
+       case R_FRV_TLSOFF_RELAX:
+       case R_FRV_TLSMOFF:
+         if (sec && (bfd_is_abs_section (sec) || bfd_is_und_section (sec)))
+           relocation += tls_biased_base (info);
+         break;
+
+       default:
+         break;
+       }
+
+      /* Try to apply TLS relaxations.  */
+      if (1)
+       switch (r_type)
+         {
+
+#define LOCAL_EXEC_P(info, picrel) \
+  (bfd_link_executable (info) \
+   && (picrel->symndx != -1 || FRVFDPIC_SYM_LOCAL ((info), (picrel)->d.h)))
+#define INITIAL_EXEC_P(info, picrel) \
+  ((bfd_link_executable (info)|| (info)->flags & DF_STATIC_TLS) \
+   && (picrel)->tlsoff_entry)
+
+#define IN_RANGE_FOR_OFST12_P(value) \
+  ((bfd_vma)((value) + 2048) < (bfd_vma)4096)
+#define IN_RANGE_FOR_SETLOS_P(value) \
+  ((bfd_vma)((value) + 32768) < (bfd_vma)65536)
+#define TLSMOFF_IN_RANGE_FOR_SETLOS_P(value, info) \
+  (IN_RANGE_FOR_SETLOS_P ((value) - tls_biased_base (info)))
+
+#define RELAX_GETTLSOFF_LOCAL_EXEC_P(info, picrel, value) \
+  (LOCAL_EXEC_P ((info), (picrel)) \
+   && TLSMOFF_IN_RANGE_FOR_SETLOS_P((value), (info)))
+#define RELAX_GETTLSOFF_INITIAL_EXEC_P(info, picrel) \
+  (INITIAL_EXEC_P ((info), (picrel)) \
+   && IN_RANGE_FOR_OFST12_P ((picrel)->tlsoff_entry))
+
+#define RELAX_TLSDESC_LOCAL_EXEC_P(info, picrel, value) \
+  (LOCAL_EXEC_P ((info), (picrel)))
+#define RELAX_TLSDESC_INITIAL_EXEC_P(info, picrel) \
+  (INITIAL_EXEC_P ((info), (picrel)))
+
+#define RELAX_GOTTLSOFF_LOCAL_EXEC_P(info, picrel, value) \
+  (LOCAL_EXEC_P ((info), (picrel)) \
+   && TLSMOFF_IN_RANGE_FOR_SETLOS_P((value), (info)))
+
+         case R_FRV_GETTLSOFF:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a call instruction?  */
+           if ((insn & (unsigned long)0x01fc0000) != 0x003c0000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GETTLSOFF not applied to a call instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_GETTLSOFF_LOCAL_EXEC_P (info, picrel,
+                                             relocation + rel->r_addend))
+             {
+               /* Replace the call instruction (except the packing bit)
+                  with setlos #tlsmofflo(symbol+offset), gr9.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x12fc0000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_GETTLSOFF_INITIAL_EXEC_P (info, picrel))
+             {
+               /* Replace the call instruction (except the packing bit)
+                  with ldi @(gr15, #gottlsoff12(symbol+addend)), gr9.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x12c8f000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_GOTTLSOFF12;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           break;
+
+         case R_FRV_GOTTLSDESC12:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this an lddi instruction?  */
+           if ((insn & (unsigned long)0x01fc0000) != 0x00cc0000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSDESC12"
+                    " not applied to an lddi instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                           relocation + rel->r_addend)
+               && TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
+                                                 info))
+             {
+               /* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
+                  with setlos #tlsmofflo(symbol+offset), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x80000000)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               insn |= (unsigned long)0x00fc0000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                                relocation + rel->r_addend))
+             {
+               /* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
+                  with sethi #tlsmoffhi(symbol+offset), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x80000000)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               insn |= (unsigned long)0x00f80000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFHI;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
+             {
+               /* Replace lddi @(grB, #gottlsdesc12(symbol+offset), grC
+                  with ldi @(grB, #gottlsoff12(symbol+offset),
+                  gr<C+1>.  Preserve the packing bit.  If gottlsoff12
+                  overflows, we'll error out, but that's sort-of ok,
+                  since we'd started with gottlsdesc12, that's actually
+                  more demanding.  Compiling with -fPIE instead of
+                  -fpie would fix it; linking with --relax should fix
+                  it as well.  */
+               insn = (insn & (unsigned long)0x80cbf000)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_GOTTLSOFF12;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           break;
+
+         case R_FRV_GOTTLSDESCHI:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a sethi instruction?  */
+           if ((insn & (unsigned long)0x01ff0000) != 0x00f80000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSDESCHI"
+                    " not applied to a sethi instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                           relocation + rel->r_addend)
+               || (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                   && IN_RANGE_FOR_SETLOS_P (picrel->tlsoff_entry)))
+             {
+               /* Replace sethi with a nop.  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
+             {
+               /* Simply decay GOTTLSDESC to GOTTLSOFF.  */
+               r_type = R_FRV_GOTTLSOFFHI;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           break;
+
+         case R_FRV_GOTTLSDESCLO:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a setlo or setlos instruction?  */
+           if ((insn & (unsigned long)0x01f70000) != 0x00f40000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSDESCLO"
+                    " not applied to a setlo or setlos instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                           relocation + rel->r_addend)
+               || (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                   && IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry)))
+             {
+               /* Replace setlo/setlos with a nop.  Preserve the
+                  packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
+             {
+               /* If the corresponding sethi (if it exists) decayed
+                  to a nop, make sure this becomes (or already is) a
+                  setlos, not setlo.  */
+               if (IN_RANGE_FOR_SETLOS_P (picrel->tlsoff_entry))
+                 {
+                   insn |= (unsigned long)0x00080000;
+                   bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+                 }
+
+               /* Simply decay GOTTLSDESC to GOTTLSOFF.  */
+               r_type = R_FRV_GOTTLSOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           break;
+
+         case R_FRV_TLSDESC_RELAX:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this an ldd instruction?  */
+           if ((insn & (unsigned long)0x01fc0fc0) != 0x00080140)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_TLSDESC_RELAX"
+                    " not applied to an ldd instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                           relocation + rel->r_addend)
+               && TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
+                                                 info))
+             {
+               /* Replace ldd #tlsdesc(symbol+offset)@(grB, grA), grC
+                  with setlos #tlsmofflo(symbol+offset), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x80000000)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               insn |= (unsigned long)0x00fc0000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                                relocation + rel->r_addend))
+             {
+               /* Replace ldd #tlsdesc(symbol+offset)@(grB, grA), grC
+                  with sethi #tlsmoffhi(symbol+offset), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x80000000)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               insn |= (unsigned long)0x00f80000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFHI;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                    && IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry))
+             {
+               /* Replace ldd #tlsdesc(symbol+offset)@(grB, grA), grC
+                  with ldi @(grB, #gottlsoff12(symbol+offset), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x8003f000)
+                 | (unsigned long)0x00c80000
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_GOTTLSOFF12;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
+             {
+               /* Replace ldd #tlsdesc(symbol+offset)@(grB, grA), grC
+                  with ld #tlsoff(symbol+offset)@(grB, grA), gr<C+1>.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0x81ffffbf)
+                 | ((insn + (unsigned long)0x02000000)
+                    & (unsigned long)0x7e000000);
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* #tlsoff(symbol+offset) is just a relaxation
+                   annotation, so there's nothing left to
+                   relocate.  */
+               continue;
+             }
+
+           break;
+
+         case R_FRV_GETTLSOFF_RELAX:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a calll or callil instruction?  */
+           if ((insn & (unsigned long)0x7ff80fc0) != 0x02300000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GETTLSOFF_RELAX"
+                    " not applied to a calll instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                           relocation + rel->r_addend)
+               && TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
+                                                 info))
+             {
+               /* Replace calll with a nop.  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           else if (RELAX_TLSDESC_LOCAL_EXEC_P (info, picrel,
+                                                relocation + rel->r_addend))
+             {
+               /* Replace calll with setlo #tlsmofflo(symbol+offset), gr9.
+                  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x12f40000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel))
+             {
+               /* Replace calll with a nop.  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           break;
+
+         case R_FRV_GOTTLSOFF12:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this an ldi instruction?  */
+           if ((insn & (unsigned long)0x01fc0000) != 0x00c80000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSOFF12"
+                    " not applied to an ldi instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_GOTTLSOFF_LOCAL_EXEC_P (info, picrel,
+                                             relocation + rel->r_addend))
+             {
+               /* Replace ldi @(grB, #gottlsoff12(symbol+offset), grC
+                  with setlos #tlsmofflo(symbol+offset), grC.
+                  Preserve the packing bit.  */
+               insn &= (unsigned long)0xfe000000;
+               insn |= (unsigned long)0x00fc0000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
+
+           break;
+
+         case R_FRV_GOTTLSOFFHI:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a sethi instruction?  */
+           if ((insn & (unsigned long)0x01ff0000) != 0x00f80000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSOFFHI"
+                    " not applied to a sethi instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_GOTTLSOFF_LOCAL_EXEC_P (info, picrel,
+                                             relocation + rel->r_addend)
+               || (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                   && IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry)))
+             {
+               /* Replace sethi with a nop.  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           break;
+
+         case R_FRV_GOTTLSOFFLO:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this a setlo or setlos instruction?  */
+           if ((insn & (unsigned long)0x01f70000) != 0x00f40000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_GOTTLSOFFLO"
+                    " not applied to a setlo or setlos instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
+
+           if (RELAX_GOTTLSOFF_LOCAL_EXEC_P (info, picrel,
+                                             relocation + rel->r_addend)
+               || (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                   && IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry)))
+             {
+               /* Replace setlo/setlos with a nop.  Preserve the
+                  packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               /* Nothing to relocate.  */
+               continue;
+             }
+
+           break;
+
+         case R_FRV_TLSOFF_RELAX:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+
+           /* Is this an ld instruction?  */
+           if ((insn & (unsigned long)0x01fc0fc0) != 0x00080100)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_TLSOFF_RELAX"
+                    " not applied to an ld instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
 
-      if (r_symndx < symtab_hdr->sh_info)
-       {
-         sym = local_syms + r_symndx;
-         osec = sec = local_sections [r_symndx];
-         relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel);
+           if (RELAX_GOTTLSOFF_LOCAL_EXEC_P (info, picrel,
+                                             relocation + rel->r_addend))
+             {
+               /* Replace ld #gottlsoff(symbol+offset)@(grB, grA), grC
+                  with setlos #tlsmofflo(symbol+offset), grC.
+                  Preserve the packing bit.  */
+               insn &= (unsigned long)0xfe000000;
+               insn |= (unsigned long)0x00fc0000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_TLSMOFFLO;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
 
-         name = bfd_elf_string_from_elf_section
-           (input_bfd, symtab_hdr->sh_link, sym->st_name);
-         name = (name == NULL) ? bfd_section_name (input_bfd, sec) : name;
-       }
-      else
-       {
-         h = sym_hashes [r_symndx - symtab_hdr->sh_info];
+           else if (RELAX_TLSDESC_INITIAL_EXEC_P (info, picrel)
+                    && IN_RANGE_FOR_OFST12_P (picrel->tlsoff_entry))
+             {
+               /* Replace ld #tlsoff(symbol+offset)@(grB, grA), grC
+                  with ldi @(grB, #gottlsoff12(symbol+offset), grC.
+                  Preserve the packing bit.  */
+               insn = (insn & (unsigned long)0xfe03f000)
+                 | (unsigned long)0x00c80000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+
+               r_type = R_FRV_GOTTLSOFF12;
+               howto  = elf32_frv_howto_table + r_type;
+               rel->r_info = ELF32_R_INFO (r_symndx, r_type);
+             }
 
-         while (h->root.type == bfd_link_hash_indirect
-                || h->root.type == bfd_link_hash_warning)
-           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+           break;
 
-         name = h->root.root.string;
+         case R_FRV_TLSMOFFHI:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
 
-         if ((h->root.type == bfd_link_hash_defined
-              || h->root.type == bfd_link_hash_defweak)
-             && ! FRVFDPIC_SYM_LOCAL (info, h))
-           {
-             sec = NULL;
-             relocation = 0;
-           }
-         else
-         if (h->root.type == bfd_link_hash_defined
-             || h->root.type == bfd_link_hash_defweak)
-           {
-             sec = h->root.u.def.section;
-             relocation = (h->root.u.def.value
-                           + sec->output_section->vma
-                           + sec->output_offset);
-           }
-         else if (h->root.type == bfd_link_hash_undefweak)
-           {
-             relocation = 0;
-           }
-         else if (info->unresolved_syms_in_objects == RM_IGNORE
-                  && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
-           relocation = 0;
-         else
-           {
-             if (! ((*info->callbacks->undefined_symbol)
-                    (info, h->root.root.string, input_bfd,
-                     input_section, rel->r_offset,
-                     (info->unresolved_syms_in_objects == RM_GENERATE_ERROR
-                      || ELF_ST_VISIBILITY (h->other)))))
+           /* Is this a sethi instruction?  */
+           if ((insn & (unsigned long)0x01ff0000) != 0x00f80000)
+             {
+               info->callbacks->einfo
+                 (_("%H: R_FRV_TLSMOFFHI"
+                    " not applied to a sethi instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
                return FALSE;
-             relocation = 0;
-           }
-         osec = sec;
-       }
+             }
 
-      switch (r_type)
-       {
-       case R_FRV_LABEL24:
-       case R_FRV_32:
-         if (! IS_FDPIC (output_bfd))
-           goto non_fdpic;
+           if (TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
+                                              info))
+             {
+               /* Replace sethi with a nop.  Preserve the packing bit.  */
+               insn &= (unsigned long)0x80000000;
+               insn |= (unsigned long)0x00880000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
 
-       case R_FRV_GOT12:
-       case R_FRV_GOTHI:
-       case R_FRV_GOTLO:
-       case R_FRV_FUNCDESC_GOT12:
-       case R_FRV_FUNCDESC_GOTHI:
-       case R_FRV_FUNCDESC_GOTLO:
-       case R_FRV_GOTOFF12:
-       case R_FRV_GOTOFFHI:
-       case R_FRV_GOTOFFLO:
-       case R_FRV_FUNCDESC_GOTOFF12:
-       case R_FRV_FUNCDESC_GOTOFFHI:
-       case R_FRV_FUNCDESC_GOTOFFLO:
-       case R_FRV_FUNCDESC:
-       case R_FRV_FUNCDESC_VALUE:
-         if (h != NULL)
-           picrel = frvfdpic_relocs_info_for_global (frvfdpic_relocs_info
-                                                     (info), input_bfd, h,
-                                                     orig_addend, INSERT);
-         else
-           /* In order to find the entry we created before, we must
-              use the original addend, not the one that may have been
-              modified by _bfd_elf_rela_local_sym().  */
-           picrel = frvfdpic_relocs_info_for_local (frvfdpic_relocs_info
-                                                    (info), input_bfd, r_symndx,
-                                                    orig_addend, INSERT);
-         if (! picrel)
-           return FALSE;
+               /* Nothing to relocate.  */
+               continue;
+             }
 
-         if (!_frvfdpic_emit_got_relocs_plt_entries (picrel, output_bfd, info,
-                                                     osec, sym,
-                                                     rel->r_addend))
-           {
-             (*_bfd_error_handler)
-               (_("%B: relocation at `%A+0x%x' references symbol `%s' with nonzero addend"),
-                input_bfd, input_section, rel->r_offset, name);
-             return FALSE;
+           break;
 
-           }
+         case R_FRV_TLSMOFFLO:
+           insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
 
-         break;
+           /* Is this a setlo or setlos instruction?  */
+           if ((insn & (unsigned long)0x01f70000) != 0x00f40000)
+             {
+               info->callbacks->einfo
+                 (_("R_FRV_TLSMOFFLO"
+                    " not applied to a setlo or setlos instruction\n"),
+                  input_bfd, input_section, rel->r_offset);
+               return FALSE;
+             }
 
-       default:
-       non_fdpic:
-         picrel = NULL;
-         if (h && ! FRVFDPIC_SYM_LOCAL (info, h))
-           {
-             info->callbacks->warning
-               (info, _("relocation references symbol not defined in the module"),
-                name, input_bfd, input_section, rel->r_offset);
-             return FALSE;
-           }
-         break;
-       }
+           if (TLSMOFF_IN_RANGE_FOR_SETLOS_P (relocation + rel->r_addend,
+                                              info))
+             /* If the corresponding sethi (if it exists) decayed
+                to a nop, make sure this becomes (or already is) a
+                setlos, not setlo.  */
+             {
+               insn |= (unsigned long)0x00080000;
+               bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+             }
+
+           break;
+
+           /*
+             There's nothing to relax in these:
+               R_FRV_TLSDESC_VALUE
+               R_FRV_TLSOFF
+               R_FRV_TLSMOFF12
+               R_FRV_TLSMOFFHI
+               R_FRV_TLSMOFFLO
+               R_FRV_TLSMOFF
+           */
+
+         default:
+           break;
+         }
 
       switch (r_type)
        {
@@ -2095,14 +3481,14 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
          relocation = picrel->got_entry;
          check_segment[0] = check_segment[1] = got_segment;
          break;
-         
+
        case R_FRV_FUNCDESC_GOT12:
        case R_FRV_FUNCDESC_GOTHI:
        case R_FRV_FUNCDESC_GOTLO:
          relocation = picrel->fdgot_entry;
          check_segment[0] = check_segment[1] = got_segment;
          break;
-         
+
        case R_FRV_GOTOFFHI:
        case R_FRV_GOTOFF12:
        case R_FRV_GOTOFFLO:
@@ -2140,7 +3526,7 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
                   section+offset.  */
                if (h && ! FRVFDPIC_FUNCDESC_LOCAL (info, h)
                    && FRVFDPIC_SYM_LOCAL (info, h)
-                   && !(info->executable && !info->pie))
+                   && !bfd_link_pde (info))
                  {
                    dynindx = elf_section_data (h->root.u.def.section
                                                ->output_section)->dynindx;
@@ -2151,9 +3537,11 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
                  {
                    if (addend)
                      {
-                       info->callbacks->warning
-                         (info, _("R_FRV_FUNCDESC references dynamic symbol with nonzero addend"),
-                          name, input_bfd, input_section, rel->r_offset);
+                       info->callbacks->einfo
+                         (_("%H: %s references dynamic symbol"
+                            " with nonzero addend\n"),
+                          input_bfd, input_section, rel->r_offset,
+                          "R_FRV_FUNCDESC");
                        return FALSE;
                      }
                    dynindx = h->dynindx;
@@ -2177,59 +3565,67 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
                   dynamic symbol entry for the got section, so idx will
                   be zero, which means we can and should compute the
                   address of the private descriptor ourselves.  */
-               if (info->executable && !info->pie
+               if (bfd_link_pde (info)
                    && (!h || FRVFDPIC_FUNCDESC_LOCAL (info, h)))
                  {
                    addend += frvfdpic_got_section (info)->output_section->vma;
-                   if ((bfd_get_section_flags (output_bfd,
-                                               input_section->output_section)
+                   if ((bfd_section_flags (input_section->output_section)
                         & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                      {
+                       bfd_vma offset;
+
                        if (_frvfdpic_osec_readonly_p (output_bfd,
                                                       input_section
                                                       ->output_section))
                          {
-                           info->callbacks->warning
-                             (info,
-                              _("cannot emit fixups in read-only section"),
-                              name, input_bfd, input_section, rel->r_offset);
+                           info->callbacks->einfo
+                             (_("%H: cannot emit fixups"
+                                " in read-only section\n"),
+                              input_bfd, input_section, rel->r_offset);
                            return FALSE;
                          }
-                       _frvfdpic_add_rofixup (output_bfd,
-                                              frvfdpic_gotfixup_section
-                                              (info),
-                                              _bfd_elf_section_offset
-                                              (output_bfd, info,
-                                               input_section, rel->r_offset)
-                                              + input_section
-                                              ->output_section->vma
-                                              + input_section->output_offset,
-                                              picrel);
+
+                       offset = _bfd_elf_section_offset
+                         (output_bfd, info,
+                          input_section, rel->r_offset);
+
+                       if (offset != (bfd_vma)-1)
+                         _frvfdpic_add_rofixup (output_bfd,
+                                                frvfdpic_gotfixup_section
+                                                (info),
+                                                offset + input_section
+                                                ->output_section->vma
+                                                + input_section->output_offset,
+                                                picrel);
                      }
                  }
-               else if ((bfd_get_section_flags (output_bfd,
-                                                input_section->output_section)
+               else if ((bfd_section_flags (input_section->output_section)
                          & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                  {
+                   bfd_vma offset;
+
                    if (_frvfdpic_osec_readonly_p (output_bfd,
                                                   input_section
                                                   ->output_section))
                      {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit dynamic relocations in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
+                       info->callbacks->einfo
+                         (_("%H: cannot emit dynamic relocations"
+                            " in read-only section\n"),
+                          input_bfd, input_section, rel->r_offset);
                        return FALSE;
                      }
-                   _frvfdpic_add_dyn_reloc (output_bfd,
-                                            frvfdpic_gotrel_section (info),
-                                            _bfd_elf_section_offset
-                                            (output_bfd, info,
-                                             input_section, rel->r_offset)
-                                            + input_section
-                                            ->output_section->vma
-                                            + input_section->output_offset,
-                                            r_type, dynindx, addend, picrel);
+
+                   offset = _bfd_elf_section_offset
+                     (output_bfd, info,
+                      input_section, rel->r_offset);
+
+                   if (offset != (bfd_vma)-1)
+                     _frvfdpic_add_dyn_reloc (output_bfd,
+                                              frvfdpic_gotrel_section (info),
+                                              offset + input_section
+                                              ->output_section->vma
+                                              + input_section->output_offset,
+                                              r_type, dynindx, addend, picrel);
                  }
                else
                  addend += frvfdpic_got_section (info)->output_section->vma;
@@ -2261,9 +3657,11 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
              {
                if (addend && r_type == R_FRV_FUNCDESC_VALUE)
                  {
-                   info->callbacks->warning
-                     (info, _("R_FRV_FUNCDESC_VALUE references dynamic symbol with nonzero addend"),
-                      name, input_bfd, input_section, rel->r_offset);
+                   info->callbacks->einfo
+                     (_("%H: %s references dynamic symbol"
+                        " with nonzero addend\n"),
+                      input_bfd, input_section, rel->r_offset,
+                      "R_FRV_FUNCDESC_VALUE");
                    return FALSE;
                  }
                dynindx = h->dynindx;
@@ -2288,75 +3686,79 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
               can omit the dynamic relocation as long as the symbol
               is defined in the current link unit (which is implied
               by its output section not being NULL).  */
-           if (info->executable && !info->pie
+           if (bfd_link_pde (info)
                && (!h || FRVFDPIC_SYM_LOCAL (info, h)))
              {
                if (osec)
                  addend += osec->output_section->vma;
                if (IS_FDPIC (input_bfd)
-                   && (bfd_get_section_flags (output_bfd,
-                                              input_section->output_section)
+                   && (bfd_section_flags (input_section->output_section)
                        & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                  {
                    if (_frvfdpic_osec_readonly_p (output_bfd,
                                                   input_section
                                                   ->output_section))
                      {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit fixups in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
+                       info->callbacks->einfo
+                         (_("%H: cannot emit fixups in read-only section\n"),
+                          input_bfd, input_section, rel->r_offset);
                        return FALSE;
                      }
                    if (!h || h->root.type != bfd_link_hash_undefweak)
                      {
-                       _frvfdpic_add_rofixup (output_bfd,
-                                              frvfdpic_gotfixup_section
-                                              (info),
-                                              _bfd_elf_section_offset
-                                              (output_bfd, info,
-                                               input_section, rel->r_offset)
-                                              + input_section
-                                              ->output_section->vma
-                                              + input_section->output_offset,
-                                              picrel);
-                       if (r_type == R_FRV_FUNCDESC_VALUE)
-                         _frvfdpic_add_rofixup
-                           (output_bfd,
-                            frvfdpic_gotfixup_section (info),
-                            _bfd_elf_section_offset
-                            (output_bfd, info,
-                             input_section, rel->r_offset)
-                            + input_section->output_section->vma
-                            + input_section->output_offset + 4, picrel);
+                       bfd_vma offset = _bfd_elf_section_offset
+                         (output_bfd, info,
+                          input_section, rel->r_offset);
+
+                       if (offset != (bfd_vma)-1)
+                         {
+                           _frvfdpic_add_rofixup (output_bfd,
+                                                  frvfdpic_gotfixup_section
+                                                  (info),
+                                                  offset + input_section
+                                                  ->output_section->vma
+                                                  + input_section->output_offset,
+                                                  picrel);
+                           if (r_type == R_FRV_FUNCDESC_VALUE)
+                             _frvfdpic_add_rofixup
+                               (output_bfd,
+                                frvfdpic_gotfixup_section (info),
+                                offset
+                                + input_section->output_section->vma
+                                + input_section->output_offset + 4, picrel);
+                         }
                      }
                  }
              }
            else
              {
-               if ((bfd_get_section_flags (output_bfd,
-                                           input_section->output_section)
+               if ((bfd_section_flags (input_section->output_section)
                     & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD))
                  {
+                   bfd_vma offset;
+
                    if (_frvfdpic_osec_readonly_p (output_bfd,
                                                   input_section
                                                   ->output_section))
                      {
-                       info->callbacks->warning
-                         (info,
-                          _("cannot emit dynamic relocations in read-only section"),
-                          name, input_bfd, input_section, rel->r_offset);
+                       info->callbacks->einfo
+                         (_("%H: cannot emit dynamic relocations"
+                            " in read-only section\n"),
+                          input_bfd, input_section, rel->r_offset);
                        return FALSE;
                      }
-                   _frvfdpic_add_dyn_reloc (output_bfd,
-                                            frvfdpic_gotrel_section (info),
-                                            _bfd_elf_section_offset
-                                            (output_bfd, info,
-                                             input_section, rel->r_offset)
-                                            + input_section
-                                            ->output_section->vma
-                                            + input_section->output_offset,
-                                            r_type, dynindx, addend, picrel);
+
+                   offset = _bfd_elf_section_offset
+                     (output_bfd, info,
+                      input_section, rel->r_offset);
+
+                   if (offset != (bfd_vma)-1)
+                     _frvfdpic_add_dyn_reloc (output_bfd,
+                                              frvfdpic_gotrel_section (info),
+                                              offset + input_section
+                                              ->output_section->vma
+                                              + input_section->output_offset,
+                                              r_type, dynindx, addend, picrel);
                  }
                else if (osec)
                  addend += osec->output_section->vma;
@@ -2371,7 +3773,7 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
                /* If we've omitted the dynamic relocation, just emit
                   the fixed addresses of the symbol and of the local
                   GOT base offset.  */
-               if (info->executable && !info->pie
+               if (bfd_link_pde (info)
                    && (!h || FRVFDPIC_SYM_LOCAL (info, h)))
                  bfd_put_32 (output_bfd,
                              frvfdpic_got_section (info)->output_section->vma
@@ -2408,6 +3810,78 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
            : (unsigned)-1;
          break;
 
+       case R_FRV_GETTLSOFF:
+         relocation = frvfdpic_plt_section (info)->output_section->vma
+           + frvfdpic_plt_section (info)->output_offset
+           + picrel->tlsplt_entry;
+         BFD_ASSERT (picrel->tlsplt_entry != (bfd_vma)-1
+                     && picrel->tlsdesc_entry);
+         check_segment[0] = isec_segment;
+         check_segment[1] = plt_segment;
+         break;
+
+       case R_FRV_GOTTLSDESC12:
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+         BFD_ASSERT (picrel->tlsdesc_entry);
+         relocation = picrel->tlsdesc_entry;
+         check_segment[0] = tls_segment;
+         check_segment[1] = sec
+           && ! bfd_is_abs_section (sec)
+           && ! bfd_is_und_section (sec)
+           ? _frvfdpic_osec_to_segment (output_bfd, sec->output_section)
+           : tls_segment;
+         break;
+
+       case R_FRV_TLSMOFF12:
+       case R_FRV_TLSMOFFHI:
+       case R_FRV_TLSMOFFLO:
+       case R_FRV_TLSMOFF:
+         check_segment[0] = tls_segment;
+         if (! sec)
+           check_segment[1] = -1;
+         else if (bfd_is_abs_section (sec)
+                  || bfd_is_und_section (sec))
+           {
+             relocation = 0;
+             check_segment[1] = tls_segment;
+           }
+         else if (sec->output_section)
+           {
+             relocation -= tls_biased_base (info);
+             check_segment[1] =
+               _frvfdpic_osec_to_segment (output_bfd, sec->output_section);
+           }
+         else
+           check_segment[1] = -1;
+         break;
+
+       case R_FRV_GOTTLSOFF12:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
+         BFD_ASSERT (picrel->tlsoff_entry);
+         relocation = picrel->tlsoff_entry;
+         check_segment[0] = tls_segment;
+         check_segment[1] = sec
+           && ! bfd_is_abs_section (sec)
+           && ! bfd_is_und_section (sec)
+           ? _frvfdpic_osec_to_segment (output_bfd, sec->output_section)
+           : tls_segment;
+         break;
+
+       case R_FRV_TLSDESC_VALUE:
+       case R_FRV_TLSOFF:
+         /* These shouldn't be present in input object files.  */
+         check_segment[0] = check_segment[1] = isec_segment;
+         break;
+
+       case R_FRV_TLSDESC_RELAX:
+       case R_FRV_GETTLSOFF_RELAX:
+       case R_FRV_TLSOFF_RELAX:
+         /* These are just annotations for relaxation, nothing to do
+            here.  */
+         continue;
+
        default:
          check_segment[0] = isec_segment;
          check_segment[1] = sec
@@ -2418,33 +3892,32 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
 
       if (check_segment[0] != check_segment[1] && IS_FDPIC (output_bfd))
        {
-#if 1 /* If you take this out, remove the #error from fdpic-static-6.d
-        in the ld testsuite.  */
+         /* If you take this out, remove the #error from fdpic-static-6.d
+            in the ld testsuite.  */
          /* This helps catch problems in GCC while we can't do more
             than static linking.  The idea is to test whether the
             input file basename is crt0.o only once.  */
          if (silence_segment_error == 1)
            silence_segment_error =
-             (strlen (input_bfd->filename) == 6
-              && strcmp (input_bfd->filename, "crt0.o") == 0)
-             || (strlen (input_bfd->filename) > 6
-                 && strcmp (input_bfd->filename
-                            + strlen (input_bfd->filename) - 7,
+             (strlen (bfd_get_filename (input_bfd)) == 6
+              && filename_cmp (bfd_get_filename (input_bfd), "crt0.o") == 0)
+             || (strlen (bfd_get_filename (input_bfd)) > 6
+                 && filename_cmp (bfd_get_filename (input_bfd)
+                                  + strlen (bfd_get_filename (input_bfd)) - 7,
                             "/crt0.o") == 0)
              ? -1 : 0;
-#endif
          if (!silence_segment_error
              /* We don't want duplicate errors for undefined
                 symbols.  */
              && !(picrel && picrel->symndx == -1
                   && picrel->d.h->root.type == bfd_link_hash_undefined))
-           info->callbacks->warning
-             (info,
-              (info->shared || info->pie)
-              ? _("relocations between different segments are not supported")
-              : _("warning: relocation references a different segment"),
-              name, input_bfd, input_section, rel->r_offset);
-         if (!silence_segment_error && (info->shared || info->pie))
+           {
+             info->callbacks->einfo
+               /* xgettext:c-format */
+               (_("%H: reloc against `%s' references a different segment\n"),
+                input_bfd, input_section, rel->r_offset, name);
+           }
+         if (!silence_segment_error && bfd_link_pic (info))
            return FALSE;
          elf_elfheader (output_bfd)->e_flags |= EF_FRV_PIC;
        }
@@ -2452,6 +3925,7 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
       switch (r_type)
        {
        case R_FRV_GOTOFFHI:
+       case R_FRV_TLSMOFFHI:
          /* We need the addend to be applied before we shift the
             value right.  */
          relocation += rel->r_addend;
@@ -2459,6 +3933,8 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_FRV_GOTHI:
        case R_FRV_FUNCDESC_GOTHI:
        case R_FRV_FUNCDESC_GOTOFFHI:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSDESCHI:
          relocation >>= 16;
          /* Fall through.  */
 
@@ -2466,6 +3942,9 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_FRV_FUNCDESC_GOTLO:
        case R_FRV_GOTOFFLO:
        case R_FRV_FUNCDESC_GOTOFFLO:
+       case R_FRV_GOTTLSOFFLO:
+       case R_FRV_GOTTLSDESCLO:
+       case R_FRV_TLSMOFFLO:
          relocation &= 0xffff;
          break;
 
@@ -2479,7 +3958,7 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
          if (! IS_FDPIC (output_bfd) || ! picrel->plt)
            break;
          /* Fall through.  */
-         
+
          /* When referencing a GOT entry, a function descriptor or a
             PLT, we don't want the addend to apply to the reference,
             but rather to the referenced symbol.  The actual entry
@@ -2494,11 +3973,19 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
        case R_FRV_FUNCDESC_GOTOFF12:
        case R_FRV_FUNCDESC_GOTOFFHI:
        case R_FRV_FUNCDESC_GOTOFFLO:
+       case R_FRV_GETTLSOFF:
+       case R_FRV_GOTTLSDESC12:
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+       case R_FRV_GOTTLSOFF12:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
          /* Note that we only want GOTOFFHI, not GOTOFFLO or GOTOFF12
             here, since we do want to apply the addend to the others.
             Note that we've applied the addend to GOTOFFHI before we
-            shifted it right.  */ 
+            shifted it right.  */
        case R_FRV_GOTOFFHI:
+       case R_FRV_TLSMOFFHI:
          relocation -= rel->r_addend;
          break;
 
@@ -2512,7 +3999,7 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
      else if (r_type == R_FRV_LO16)
        r = elf32_frv_relocate_lo16 (input_bfd, rel, contents, relocation);
 
-     else if (r_type == R_FRV_LABEL24)
+     else if (r_type == R_FRV_LABEL24 || r_type == R_FRV_GETTLSOFF)
        r = elf32_frv_relocate_label24 (input_bfd, input_section, rel,
                                       contents, relocation);
 
@@ -2532,6 +4019,10 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
        r = elf32_frv_relocate_gprelhi (info, input_bfd, input_section, rel,
                                       contents, relocation);
 
+     else if (r_type == R_FRV_TLSOFF
+             || r_type == R_FRV_TLSDESC_VALUE)
+       r = bfd_reloc_notsupported;
+
      else
        r = frv_final_link_relocate (howto, input_bfd, input_section, contents,
                                    rel, relocation);
@@ -2543,13 +4034,13 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
          switch (r)
            {
            case bfd_reloc_overflow:
-             r = info->callbacks->reloc_overflow
-               (info, name, howto->name, (bfd_vma) 0,
-                input_bfd, input_section, rel->r_offset);
+             (*info->callbacks->reloc_overflow)
+               (info, (h ? &h->root : NULL), name, howto->name,
+                (bfd_vma) 0, input_bfd, input_section, rel->r_offset);
              break;
 
            case bfd_reloc_undefined:
-             r = info->callbacks->undefined_symbol
+             (*info->callbacks->undefined_symbol)
                (info, name, input_bfd, input_section, rel->r_offset, TRUE);
              break;
 
@@ -2571,11 +4062,13 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
            }
 
          if (msg)
-           r = info->callbacks->warning
-             (info, msg, name, input_bfd, input_section, rel->r_offset);
-
-         if (! r)
-           return FALSE;
+           {
+             info->callbacks->einfo
+               /* xgettext:c-format */
+               (_("%H: reloc against `%s': %s\n"),
+                input_bfd, input_section, rel->r_offset, name, msg);
+             return FALSE;
+           }
        }
     }
 
@@ -2586,70 +4079,37 @@ elf32_frv_relocate_section (output_bfd, info, input_bfd, input_section,
    relocation.  */
 
 static asection *
-elf32_frv_gc_mark_hook (sec, info, rel, h, sym)
-     asection *sec;
-     struct bfd_link_info *info ATTRIBUTE_UNUSED;
-     Elf_Internal_Rela *rel;
-     struct elf_link_hash_entry *h;
-     Elf_Internal_Sym *sym;
+elf32_frv_gc_mark_hook (asection *sec,
+                       struct bfd_link_info *info,
+                       Elf_Internal_Rela *rel,
+                       struct elf_link_hash_entry *h,
+                       Elf_Internal_Sym *sym)
 {
   if (h != NULL)
-    {
-      switch (ELF32_R_TYPE (rel->r_info))
-       {
-       case R_FRV_GNU_VTINHERIT:
-       case R_FRV_GNU_VTENTRY:
-         break;
-
-       default:
-         switch (h->root.type)
-           {
-           default:
-             break;
-
-           case bfd_link_hash_defined:
-           case bfd_link_hash_defweak:
-             return h->root.u.def.section;
-
-           case bfd_link_hash_common:
-             return h->root.u.c.p->section;
-           }
-       }
-    }
-  else
-    return bfd_section_from_elf_index (sec->owner, sym->st_shndx);
-
-  return NULL;
-}
-
-/* Update the got entry reference counts for the section being removed.  */
+    switch (ELF32_R_TYPE (rel->r_info))
+      {
+      case R_FRV_GNU_VTINHERIT:
+      case R_FRV_GNU_VTENTRY:
+       return NULL;
+      }
 
-static bfd_boolean
-elf32_frv_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;
-{
-  return TRUE;
+  return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
 }
-
 \f
 /* Hook called by the linker routine which adds symbols from an object
    file.  We use it to put .comm items in .scomm, and not .comm.  */
 
 static bfd_boolean
-elf32_frv_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     Elf_Internal_Sym *sym;
-     const char **namep ATTRIBUTE_UNUSED;
-     flagword *flagsp ATTRIBUTE_UNUSED;
-     asection **secp;
-     bfd_vma *valp;
+elf32_frv_add_symbol_hook (bfd *abfd,
+                          struct bfd_link_info *info,
+                          Elf_Internal_Sym *sym,
+                          const char **namep ATTRIBUTE_UNUSED,
+                          flagword *flagsp ATTRIBUTE_UNUSED,
+                          asection **secp,
+                          bfd_vma *valp)
 {
   if (sym->st_shndx == SHN_COMMON
-      && !info->relocatable
+      && !bfd_link_relocatable (info)
       && (int)sym->st_size <= (int)bfd_get_gp_size (abfd))
     {
       /* Common symbols less than or equal to -G nn bytes are
@@ -2659,11 +4119,11 @@ elf32_frv_add_symbol_hook (abfd, info, sym, namep, flagsp, secp, valp)
 
       if (scomm == NULL)
        {
-         scomm = bfd_make_section (abfd, ".scommon");
-         if (scomm == NULL
-             || !bfd_set_section_flags (abfd, scomm, (SEC_ALLOC
-                                                      | SEC_IS_COMMON
-                                                      | SEC_LINKER_CREATED)))
+         scomm = bfd_make_section_with_flags (abfd, ".scommon",
+                                              (SEC_ALLOC
+                                               | SEC_IS_COMMON
+                                               | SEC_LINKER_CREATED));
+         if (scomm == NULL)
            return FALSE;
        }
 
@@ -2705,7 +4165,7 @@ _frvfdpic_link_omit_section_dynsym (bfd *output_bfd ATTRIBUTE_UNUSED,
 static bfd_boolean
 _frv_create_got_section (bfd *abfd, struct bfd_link_info *info)
 {
-  flagword flags;
+  flagword flags, pltflags;
   asection *s;
   struct elf_link_hash_entry *h;
   struct bfd_link_hash_entry *bh;
@@ -2714,8 +4174,8 @@ _frv_create_got_section (bfd *abfd, struct bfd_link_info *info)
   int offset;
 
   /* This function may be called more than once.  */
-  s = bfd_get_section_by_name (abfd, ".got");
-  if (s != NULL && (s->flags & SEC_LINKER_CREATED) != 0)
+  s = elf_hash_table (info)->sgot;
+  if (s != NULL)
     return TRUE;
 
   /* Machine specific: although pointers are 32-bits wide, we want the
@@ -2726,73 +4186,57 @@ _frv_create_got_section (bfd *abfd, struct bfd_link_info *info)
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
           | SEC_LINKER_CREATED);
+  pltflags = flags;
 
-  s = bfd_make_section (abfd, ".got");
+  s = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
+  elf_hash_table (info)->sgot = s;
   if (s == NULL
-      || !bfd_set_section_flags (abfd, s, flags)
-      || !bfd_set_section_alignment (abfd, s, ptralign))
+      || !bfd_set_section_alignment (s, ptralign))
     return FALSE;
 
-  if (bed->want_got_plt)
-    {
-      s = bfd_make_section (abfd, ".got.plt");
-      if (s == NULL
-         || !bfd_set_section_flags (abfd, s, flags)
-         || !bfd_set_section_alignment (abfd, s, ptralign))
-       return FALSE;
-    }
-
   if (bed->want_got_sym)
     {
       /* Define the symbol _GLOBAL_OFFSET_TABLE_ at the start of the .got
         (or .got.plt) section.  We don't do this in the linker script
         because we don't want to define the symbol if we are not creating
         a global offset table.  */
-      bh = NULL;
-      if (!(_bfd_generic_link_add_one_symbol
-           (info, abfd, "_GLOBAL_OFFSET_TABLE_", BSF_GLOBAL, s,
-            bed->got_symbol_offset, (const char *) NULL, FALSE,
-            bed->collect, &bh)))
+      h = _bfd_elf_define_linkage_sym (abfd, info, s, "_GLOBAL_OFFSET_TABLE_");
+      elf_hash_table (info)->hgot = h;
+      if (h == NULL)
        return FALSE;
-      h = (struct elf_link_hash_entry *) bh;
-      h->def_regular = 1;
-      h->type = STT_OBJECT;
 
       /* Machine-specific: we want the symbol for executables as
         well.  */
       if (! bfd_elf_link_record_dynamic_symbol (info, h))
        return FALSE;
-
-      elf_hash_table (info)->hgot = h;
     }
 
   /* The first bit of the global offset table is the header.  */
-  s->size += bed->got_header_size + bed->got_symbol_offset;
+  s->size += bed->got_header_size;
 
   /* This is the machine-specific part.  Create and initialize section
      data for the got.  */
   if (IS_FDPIC (abfd))
     {
-      frvfdpic_got_section (info) = s;
-      frvfdpic_relocs_info (info) = htab_try_create (1, frvfdpic_relocs_info_hash,
+      frvfdpic_relocs_info (info) = htab_try_create (1,
+                                                    frvfdpic_relocs_info_hash,
                                                     frvfdpic_relocs_info_eq,
                                                     (htab_del) NULL);
       if (! frvfdpic_relocs_info (info))
        return FALSE;
 
-      s = bfd_make_section (abfd, ".rel.got");
+      s = bfd_make_section_anyway_with_flags (abfd, ".rel.got",
+                                             (flags | SEC_READONLY));
+      elf_hash_table (info)->srelgot = s;
       if (s == NULL
-         || ! bfd_set_section_flags (abfd, s, (flags | SEC_READONLY))
-         || ! bfd_set_section_alignment (abfd, s, 2))
+         || !bfd_set_section_alignment (s, 2))
        return FALSE;
 
-      frvfdpic_gotrel_section (info) = s;
-
       /* Machine-specific.  */
-      s = bfd_make_section (abfd, ".rofixup");
+      s = bfd_make_section_anyway_with_flags (abfd, ".rofixup",
+                                             (flags | SEC_READONLY));
       if (s == NULL
-         || ! bfd_set_section_flags (abfd, s, (flags | SEC_READONLY))
-         || ! bfd_set_section_alignment (abfd, s, 2))
+         || !bfd_set_section_alignment (s, 2))
        return FALSE;
 
       frvfdpic_gotfixup_section (info) = s;
@@ -2816,77 +4260,78 @@ _frv_create_got_section (bfd *abfd, struct bfd_link_info *info)
   h = (struct elf_link_hash_entry *) bh;
   h->def_regular = 1;
   h->type = STT_OBJECT;
+  /* h->other = STV_HIDDEN; */ /* Should we?  */
 
   /* Machine-specific: we want the symbol for executables as well.  */
   if (IS_FDPIC (abfd) && ! bfd_elf_link_record_dynamic_symbol (info, h))
     return FALSE;
 
-  return TRUE;
-}
+  if (!IS_FDPIC (abfd))
+    return TRUE;
 
-/* Make sure the got and plt sections exist, and that our pointers in
-   the link hash table point to them.  */
+  /* FDPIC supports Thread Local Storage, and this may require a
+     procedure linkage table for TLS PLT entries.  */
 
-static bfd_boolean
-elf32_frvfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
-{
   /* This is mostly copied from
      elflink.c:_bfd_elf_create_dynamic_sections().  */
-  flagword flags, pltflags;
-  asection *s;
-  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-
-  /* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and
-     .rel[a].bss sections.  */
 
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-          | SEC_LINKER_CREATED);
-
-  pltflags = flags;
+  flags = pltflags;
   pltflags |= SEC_CODE;
   if (bed->plt_not_loaded)
     pltflags &= ~ (SEC_CODE | SEC_LOAD | SEC_HAS_CONTENTS);
   if (bed->plt_readonly)
     pltflags |= SEC_READONLY;
 
-  s = bfd_make_section (abfd, ".plt");
+  s = bfd_make_section_anyway_with_flags (abfd, ".plt", pltflags);
   if (s == NULL
-      || ! bfd_set_section_flags (abfd, s, pltflags)
-      || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment))
+      || !bfd_set_section_alignment (s, bed->plt_alignment))
     return FALSE;
   /* FRV-specific: remember it.  */
   frvfdpic_plt_section (info) = s;
 
+  /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
+     .plt section.  */
   if (bed->want_plt_sym)
     {
-      /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
-        .plt section.  */
-      struct elf_link_hash_entry *h;
-      struct bfd_link_hash_entry *bh = NULL;
-
-      if (! (_bfd_generic_link_add_one_symbol
-            (info, abfd, "_PROCEDURE_LINKAGE_TABLE_", BSF_GLOBAL, s, 0, NULL,
-             FALSE, get_elf_backend_data (abfd)->collect, &bh)))
-       return FALSE;
-      h = (struct elf_link_hash_entry *) bh;
-      h->def_regular = 1;
-      h->type = STT_OBJECT;
-
-      if (! info->executable
-         && ! bfd_elf_link_record_dynamic_symbol (info, h))
+      h = _bfd_elf_define_linkage_sym (abfd, info, s,
+                                      "_PROCEDURE_LINKAGE_TABLE_");
+      elf_hash_table (info)->hplt = h;
+      if (h == NULL)
        return FALSE;
     }
 
   /* FRV-specific: we want rel relocations for the plt.  */
-  s = bfd_make_section (abfd, ".rel.plt");
+  s = bfd_make_section_anyway_with_flags (abfd, ".rel.plt",
+                                         flags | SEC_READONLY);
   if (s == NULL
-      || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
-      || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+      || !bfd_set_section_alignment (s, bed->s->log_file_align))
     return FALSE;
   /* FRV-specific: remember it.  */
   frvfdpic_pltrel_section (info) = s;
 
-  /* FRV-specific: we want to create the GOT in the FRV way.  */
+  return TRUE;
+}
+
+/* Make sure the got and plt sections exist, and that our pointers in
+   the link hash table point to them.  */
+
+static bfd_boolean
+elf32_frvfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  /* This is mostly copied from
+     elflink.c:_bfd_elf_create_dynamic_sections().  */
+  flagword flags;
+  asection *s;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+          | SEC_LINKER_CREATED);
+
+  /* We need to create .plt, .rel[a].plt, .got, .got.plt, .dynbss, and
+     .rel[a].bss sections.  */
+
+  /* FRV-specific: we want to create the GOT and the PLT in the FRV
+     way.  */
   if (! _frv_create_got_section (abfd, info))
     return FALSE;
 
@@ -2904,9 +4349,9 @@ elf32_frvfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
         image and use a R_*_COPY reloc to tell the dynamic linker to
         initialize them at run time.  The linker script puts the .dynbss
         section into the .bss section of the final image.  */
-      s = bfd_make_section (abfd, ".dynbss");
-      if (s == NULL
-         || ! bfd_set_section_flags (abfd, s, SEC_ALLOC | SEC_LINKER_CREATED))
+      s = bfd_make_section_anyway_with_flags (abfd, ".dynbss",
+                                             SEC_ALLOC | SEC_LINKER_CREATED);
+      if (s == NULL)
        return FALSE;
 
       /* The .rel[a].bss section holds copy relocs.  This section is not
@@ -2920,64 +4365,31 @@ elf32_frvfdpic_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
      be needed, we can discard it later.  We will never need this
      section when generating a shared object, since they do not use
      copy relocs.  */
-      if (! info->shared)
+      if (! bfd_link_pic (info))
        {
-         s = bfd_make_section (abfd,
-                               (bed->default_use_rela_p
-                                ? ".rela.bss" : ".rel.bss"));
+         s = bfd_make_section_anyway_with_flags (abfd,
+                                                 (bed->default_use_rela_p
+                                                  ? ".rela.bss" : ".rel.bss"),
+                                                 flags | SEC_READONLY);
          if (s == NULL
-             || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
-             || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+             || !bfd_set_section_alignment (s, bed->s->log_file_align))
            return FALSE;
        }
     }
 
-  return TRUE;
-}
-
-/* The name of the dynamic interpreter.  This is put in the .interp
-   section.  */
-
-#define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1"
-
-#define DEFAULT_STACK_SIZE 0x20000
-
-/* This structure is used to collect the number of entries present in
-   each addressable range of the got.  */
-struct _frvfdpic_dynamic_got_info
-{
-  /* Several bits of information about the current link.  */
-  struct bfd_link_info *info;
-  /* Total size needed for GOT entries within the 12-, 16- or 32-bit
-     ranges.  */
-  bfd_vma got12, gotlos, gothilo;
-  /* Total size needed for function descriptor entries within the 12-,
-     16- or 32-bit ranges.  */
-  bfd_vma fd12, fdlos, fdhilo;
-  /* Total size needed function descriptor entries referenced in PLT
-     entries, that would be profitable to place in offsets close to
-     the PIC register.  */
-  bfd_vma fdplt;
-  /* Total size needed by lazy PLT entries.  */
-  bfd_vma lzplt;
-  /* Number of relocations carried over from input object files.  */
-  unsigned long relocs;
-  /* Number of fixups introduced by relocations in input object files.  */
-  unsigned long fixups;
-};
+  return TRUE;
+}
 
-/* Compute the total GOT size required by each symbol in each range.
-   Symbols may require up to 4 words in the GOT: an entry pointing to
-   the symbol, an entry pointing to its function descriptor, and a
-   private function descriptors taking two words.  */
+/* Compute the total GOT and PLT size required by each symbol in each
+   range.  Symbols may require up to 4 words in the GOT: an entry
+   pointing to the symbol, an entry pointing to its function
+   descriptor, and a private function descriptors taking two
+   words.  */
 
-static int
-_frvfdpic_count_got_plt_entries (void **entryp, void *dinfo_)
+static void
+_frvfdpic_count_nontls_entries (struct frvfdpic_relocs_info *entry,
+                               struct _frvfdpic_dynamic_got_info *dinfo)
 {
-  struct frvfdpic_relocs_info *entry = *entryp;
-  struct _frvfdpic_dynamic_got_info *dinfo = dinfo_;
-  unsigned relocs = 0, fixups = 0;
-
   /* Allocate space for a GOT entry pointing to the symbol.  */
   if (entry->got12)
     dinfo->got12 += 4;
@@ -3031,9 +4443,82 @@ _frvfdpic_count_got_plt_entries (void **entryp, void *dinfo_)
 
   if (entry->lazyplt)
     dinfo->lzplt += 8;
+}
+
+/* Compute the total GOT size required by each TLS symbol in each
+   range.  Symbols may require up to 5 words in the GOT: an entry
+   holding the TLS offset for the symbol, and an entry with a full TLS
+   descriptor taking 4 words.  */
+
+static void
+_frvfdpic_count_tls_entries (struct frvfdpic_relocs_info *entry,
+                            struct _frvfdpic_dynamic_got_info *dinfo,
+                            bfd_boolean subtract)
+{
+  const int l = subtract ? -1 : 1;
+
+  /* Allocate space for a GOT entry with the TLS offset of the
+     symbol.  */
+  if (entry->tlsoff12)
+    dinfo->got12 += 4 * l;
+  else if (entry->tlsofflos)
+    dinfo->gotlos += 4 * l;
+  else if (entry->tlsoffhilo)
+    dinfo->gothilo += 4 * l;
+  else
+    entry->relocstlsoff -= l;
+  entry->relocstlsoff += l;
+
+  /* If there's any TLSOFF relocation, mark the output file as not
+     suitable for dlopening.  This mark will remain even if we relax
+     all such relocations, but this is not a problem, since we'll only
+     do so for executables, and we definitely don't want anyone
+     dlopening executables.  */
+  if (entry->relocstlsoff)
+    dinfo->info->flags |= DF_STATIC_TLS;
+
+  /* Allocate space for a TLS descriptor.  */
+  if (entry->tlsdesc12)
+    dinfo->tlsd12 += 8 * l;
+  else if (entry->tlsdesclos)
+    dinfo->tlsdlos += 8 * l;
+  else if (entry->tlsplt)
+    dinfo->tlsdplt += 8 * l;
+  else if (entry->tlsdeschilo)
+    dinfo->tlsdhilo += 8 * l;
+  else
+    entry->relocstlsd -= l;
+  entry->relocstlsd += l;
+}
+
+/* Compute the number of dynamic relocations and fixups that a symbol
+   requires, and add (or subtract) from the grand and per-symbol
+   totals.  */
+
+static void
+_frvfdpic_count_relocs_fixups (struct frvfdpic_relocs_info *entry,
+                              struct _frvfdpic_dynamic_got_info *dinfo,
+                              bfd_boolean subtract)
+{
+  bfd_vma relocs = 0, fixups = 0, tlsrets = 0;
 
-  if (!dinfo->info->executable || dinfo->info->pie)
-    relocs = entry->relocs32 + entry->relocsfd + entry->relocsfdv;
+  if (!bfd_link_pde (dinfo->info))
+    {
+      relocs = entry->relocs32 + entry->relocsfd + entry->relocsfdv
+       + entry->relocstlsd;
+
+      /* In the executable, TLS relocations to symbols that bind
+        locally (including those that resolve to global TLS offsets)
+        are resolved immediately, without any need for fixups or
+        dynamic relocations.  In shared libraries, however, we must
+        emit dynamic relocations even for local symbols, because we
+        don't know the module id the library is going to get at
+        run-time, nor its TLS base offset.  */
+      if (!bfd_link_executable (dinfo->info)
+         || (entry->symndx == -1
+             && ! FRVFDPIC_SYM_LOCAL (dinfo->info, entry->d.h)))
+       relocs += entry->relocstlsoff;
+    }
   else
     {
       if (entry->symndx != -1 || FRVFDPIC_SYM_LOCAL (dinfo->info, entry->d.h))
@@ -3041,9 +4526,14 @@ _frvfdpic_count_got_plt_entries (void **entryp, void *dinfo_)
          if (entry->symndx != -1
              || entry->d.h->root.type != bfd_link_hash_undefweak)
            fixups += entry->relocs32 + 2 * entry->relocsfdv;
+         fixups += entry->relocstlsd;
+         tlsrets += entry->relocstlsd;
        }
       else
-       relocs += entry->relocs32 + entry->relocsfdv;
+       {
+         relocs += entry->relocs32 + entry->relocsfdv
+           + entry->relocstlsoff + entry->relocstlsd;
+       }
 
       if (entry->symndx != -1
          || FRVFDPIC_FUNCDESC_LOCAL (dinfo->info, entry->d.h))
@@ -3056,42 +4546,160 @@ _frvfdpic_count_got_plt_entries (void **entryp, void *dinfo_)
        relocs += entry->relocsfd;
     }
 
+  if (subtract)
+    {
+      relocs = - relocs;
+      fixups = - fixups;
+      tlsrets = - tlsrets;
+    }
+
   entry->dynrelocs += relocs;
   entry->fixups += fixups;
   dinfo->relocs += relocs;
   dinfo->fixups += fixups;
+  dinfo->tls_ret_refs += tlsrets;
+}
 
-  return 1;
+/* Look for opportunities to relax TLS relocations.  We can assume
+   we're linking the main executable or a static-tls library, since
+   otherwise we wouldn't have got here.  When relaxing, we have to
+   first undo any previous accounting of TLS uses of fixups, dynamic
+   relocations, GOT and PLT entries.  */
+
+static void
+_frvfdpic_relax_tls_entries (struct frvfdpic_relocs_info *entry,
+                            struct _frvfdpic_dynamic_got_info *dinfo,
+                            bfd_boolean relaxing)
+{
+  bfd_boolean changed = ! relaxing;
+
+  BFD_ASSERT (bfd_link_executable (dinfo->info)
+             || (dinfo->info->flags & DF_STATIC_TLS));
+
+  if (entry->tlsdesc12 || entry->tlsdesclos || entry->tlsdeschilo)
+    {
+      if (! changed)
+       {
+         _frvfdpic_count_relocs_fixups (entry, dinfo, TRUE);
+         _frvfdpic_count_tls_entries (entry, dinfo, TRUE);
+         changed = TRUE;
+       }
+
+      /* When linking an executable, we can always decay GOTTLSDESC to
+        TLSMOFF, if the symbol is local, or GOTTLSOFF, otherwise.
+        When linking a static-tls shared library, using TLSMOFF is
+        not an option, but we can still use GOTTLSOFF.  When decaying
+        to GOTTLSOFF, we must keep the GOT entry in range.  We know
+        it has to fit because we'll be trading the 4 words of hte TLS
+        descriptor for a single word in the same range.  */
+      if (! bfd_link_executable (dinfo->info)
+         || (entry->symndx == -1
+             && ! FRVFDPIC_SYM_LOCAL (dinfo->info, entry->d.h)))
+       {
+         entry->tlsoff12 |= entry->tlsdesc12;
+         entry->tlsofflos |= entry->tlsdesclos;
+         entry->tlsoffhilo |= entry->tlsdeschilo;
+       }
+
+      entry->tlsdesc12 = entry->tlsdesclos = entry->tlsdeschilo = 0;
+    }
+
+  /* We can only decay TLSOFFs or call #gettlsoff to TLSMOFF in the
+     main executable.  We have to check whether the symbol's TLSOFF is
+     in range for a setlos.  For symbols with a hash entry, we can
+     determine exactly what to do; for others locals, we don't have
+     addresses handy, so we use the size of the TLS section as an
+     approximation.  If we get it wrong, we'll retain a GOT entry
+     holding the TLS offset (without dynamic relocations or fixups),
+     but we'll still optimize away the loads from it.  Since TLS sizes
+     are generally very small, it's probably not worth attempting to
+     do better than this.  */
+  if ((entry->tlsplt
+       || entry->tlsoff12 || entry->tlsofflos || entry->tlsoffhilo)
+      && bfd_link_executable (dinfo->info) && relaxing
+      && ((entry->symndx == -1
+          && FRVFDPIC_SYM_LOCAL (dinfo->info, entry->d.h)
+          /* The above may hold for an undefweak TLS symbol, so make
+             sure we don't have this case before accessing def.value
+             and def.section.  */
+          && (entry->d.h->root.type == bfd_link_hash_undefweak
+              || (bfd_vma)(entry->d.h->root.u.def.value
+                           + (entry->d.h->root.u.def.section
+                              ->output_section->vma)
+                           + entry->d.h->root.u.def.section->output_offset
+                           + entry->addend
+                           - tls_biased_base (dinfo->info)
+                           + 32768) < (bfd_vma)65536))
+         || (entry->symndx != -1
+             && (elf_hash_table (dinfo->info)->tls_sec->size
+                 + entry->addend < 32768 + FRVFDPIC_TLS_BIAS))))
+    {
+      if (! changed)
+       {
+         _frvfdpic_count_relocs_fixups (entry, dinfo, TRUE);
+         _frvfdpic_count_tls_entries (entry, dinfo, TRUE);
+         changed = TRUE;
+       }
+
+      entry->tlsplt =
+       entry->tlsoff12 = entry->tlsofflos = entry->tlsoffhilo = 0;
+    }
+
+  /* We can decay `call #gettlsoff' to a ldi #tlsoff if we already
+     have a #gottlsoff12 relocation for this entry, or if we can fit
+     one more in the 12-bit (and 16-bit) ranges.  */
+  if (entry->tlsplt
+      && (entry->tlsoff12
+         || (relaxing
+             && dinfo->got12 + dinfo->fd12 + dinfo->tlsd12 <= 4096 - 12 - 4
+             && (dinfo->got12 + dinfo->fd12 + dinfo->tlsd12
+                 + dinfo->gotlos + dinfo->fdlos + dinfo->tlsdlos
+                 <= 65536 - 12 - 4))))
+    {
+      if (! changed)
+       {
+         _frvfdpic_count_relocs_fixups (entry, dinfo, TRUE);
+         _frvfdpic_count_tls_entries (entry, dinfo, TRUE);
+         changed = TRUE;
+       }
+
+      entry->tlsoff12 = 1;
+      entry->tlsplt = 0;
+    }
+
+  if (changed)
+    {
+      _frvfdpic_count_tls_entries (entry, dinfo, FALSE);
+      _frvfdpic_count_relocs_fixups (entry, dinfo, FALSE);
+    }
+
+  return;
 }
 
-/* This structure is used to assign offsets to got entries, function
-   descriptors, plt entries and lazy plt entries.  */
+/* Compute the total GOT and PLT size required by each symbol in each range. *
+   Symbols may require up to 4 words in the GOT: an entry pointing to
+   the symbol, an entry pointing to its function descriptor, and a
+   private function descriptors taking two words.  */
 
-struct _frvfdpic_dynamic_got_plt_info
+static int
+_frvfdpic_count_got_plt_entries (void **entryp, void *dinfo_)
 {
-  /* Summary information collected with _frvfdpic_count_got_plt_entries.  */
-  struct _frvfdpic_dynamic_got_info g;
+  struct frvfdpic_relocs_info *entry = *entryp;
+  struct _frvfdpic_dynamic_got_info *dinfo = dinfo_;
 
-  /* For each addressable range, we record a MAX (positive) and MIN
-     (negative) value.  CUR is used to assign got entries, and it's
-     incremented from an initial positive value to MAX, then from MIN
-     to FDCUR (unless FDCUR wraps around first).  FDCUR is used to
-     assign function descriptors, and it's decreased from an initial
-     non-positive value to MIN, then from MAX down to CUR (unless CUR
-     wraps around first).  All of MIN, MAX, CUR and FDCUR always point
-     to even words.  ODD, if non-zero, indicates an odd word to be
-     used for the next got entry, otherwise CUR is used and
-     incremented by a pair of words, wrapping around when it reaches
-     MAX.  FDCUR is decremented (and wrapped) before the next function
-     descriptor is chosen.  FDPLT indicates the number of remaining
-     slots that can be used for function descriptors used only by PLT
-     entries.  */
-  struct _frvfdpic_dynamic_got_alloc_data
-  {
-    bfd_signed_vma max, cur, odd, fdcur, min;
-    bfd_vma fdplt;
-  } got12, gotlos, gothilo;
-};
+  _frvfdpic_count_nontls_entries (entry, dinfo);
+
+  if (bfd_link_executable (dinfo->info)
+      || (dinfo->info->flags & DF_STATIC_TLS))
+    _frvfdpic_relax_tls_entries (entry, dinfo, FALSE);
+  else
+    {
+      _frvfdpic_count_tls_entries (entry, dinfo, FALSE);
+      _frvfdpic_count_relocs_fixups (entry, dinfo, FALSE);
+    }
+
+  return 1;
+}
 
 /* Determine the positive and negative ranges to be used by each
    offset range in the GOT.  FDCUR and CUR, that must be aligned to a
@@ -3111,9 +4719,12 @@ _frvfdpic_compute_got_alloc_data (struct _frvfdpic_dynamic_got_alloc_data *gad,
                                  bfd_vma got,
                                  bfd_vma fd,
                                  bfd_vma fdplt,
+                                 bfd_vma tlsd,
+                                 bfd_vma tlsdplt,
                                  bfd_vma wrap)
 {
   bfd_signed_vma wrapmin = -wrap;
+  const bfd_vma tdescsz = 8;
 
   /* Start at the given initial points.  */
   gad->fdcur = fdcur;
@@ -3147,7 +4758,7 @@ _frvfdpic_compute_got_alloc_data (struct _frvfdpic_dynamic_got_alloc_data *gad,
       odd = cur + got;
       got += 4;
     }
-  
+
   /* Compute the tentative boundaries of this range.  */
   gad->max = cur + got;
   gad->min = fdcur - fd;
@@ -3158,48 +4769,109 @@ _frvfdpic_compute_got_alloc_data (struct _frvfdpic_dynamic_got_alloc_data *gad,
   if (gad->min < wrapmin)
     {
       gad->max += wrapmin - gad->min;
-      gad->min = wrapmin;
+      gad->tmin = gad->min = wrapmin;
+    }
+
+  /* If GOT entries took too much space, wrap some of them around.
+     This may well cause gad->min to become lower than wrapmin.  This
+     will cause a relocation overflow later on, so we don't have to
+     report it here . */
+  if ((bfd_vma) gad->max > wrap)
+    {
+      gad->min -= gad->max - wrap;
+      gad->max = wrap;
+    }
+
+  /* Add TLS descriptors.  */
+  gad->tmax = gad->max + tlsd;
+  gad->tmin = gad->min;
+  gad->tlsdplt = 0;
+
+  /* If TLS descriptors took too much space, wrap an integral number
+     of them around.  */
+  if ((bfd_vma) gad->tmax > wrap)
+    {
+      bfd_vma wrapsize = gad->tmax - wrap;
+
+      wrapsize += tdescsz / 2;
+      wrapsize &= ~ tdescsz / 2;
+
+      gad->tmin -= wrapsize;
+      gad->tmax -= wrapsize;
     }
+
   /* If there is space left and we have function descriptors
      referenced in PLT entries that could take advantage of shorter
-     offsets, place them here.  */
-  else if (fdplt && gad->min > wrapmin)
+     offsets, place them now.  */
+  if (fdplt && gad->tmin > wrapmin)
     {
       bfd_vma fds;
-      if ((bfd_vma) (gad->min - wrapmin) < fdplt)
-       fds = gad->min - wrapmin;
+
+      if ((bfd_vma) (gad->tmin - wrapmin) < fdplt)
+       fds = gad->tmin - wrapmin;
       else
        fds = fdplt;
 
       fdplt -= fds;
       gad->min -= fds;
+      gad->tmin -= fds;
       gad->fdplt += fds;
     }
 
-  /* If GOT entries took too much space, wrap some of them around.
-     This may well cause gad->min to become lower than wrapmin.  This
-     will cause a relocation overflow later on, so we don't have to
-     report it here . */
-  if ((bfd_vma) gad->max > wrap)
-    {
-      gad->min -= gad->max - wrap;
-      gad->max = wrap;
-    }
   /* If there is more space left, try to place some more function
      descriptors for PLT entries.  */
-  else if (fdplt && (bfd_vma) gad->max < wrap)
+  if (fdplt && (bfd_vma) gad->tmax < wrap)
     {
       bfd_vma fds;
-      if ((bfd_vma) (wrap - gad->max) < fdplt)
-       fds = wrap - gad->max;
+
+      if ((bfd_vma) (wrap - gad->tmax) < fdplt)
+       fds = wrap - gad->tmax;
       else
        fds = fdplt;
 
       fdplt -= fds;
       gad->max += fds;
+      gad->tmax += fds;
       gad->fdplt += fds;
     }
 
+  /* If there is space left and we have TLS descriptors referenced in
+     PLT entries that could take advantage of shorter offsets, place
+     them now.  */
+  if (tlsdplt && gad->tmin > wrapmin)
+    {
+      bfd_vma tlsds;
+
+      if ((bfd_vma) (gad->tmin - wrapmin) < tlsdplt)
+       tlsds = (gad->tmin - wrapmin) & ~ (tdescsz / 2);
+      else
+       tlsds = tlsdplt;
+
+      tlsdplt -= tlsds;
+      gad->tmin -= tlsds;
+      gad->tlsdplt += tlsds;
+    }
+
+  /* If there is more space left, try to place some more TLS
+     descriptors for PLT entries.  Although we could try to fit an
+     additional TLS descriptor with half of it just before before the
+     wrap point and another right past the wrap point, this might
+     cause us to run out of space for the next region, so don't do
+     it.  */
+  if (tlsdplt && (bfd_vma) gad->tmax < wrap - tdescsz / 2)
+    {
+      bfd_vma tlsds;
+
+      if ((bfd_vma) (wrap - gad->tmax) < tlsdplt)
+       tlsds = (wrap - gad->tmax) & ~ (tdescsz / 2);
+      else
+       tlsds = tlsdplt;
+
+      tlsdplt -= tlsds;
+      gad->tmax += tlsds;
+      gad->tlsdplt += tlsds;
+    }
+
   /* If odd was initially computed as an offset past the wrap point,
      wrap it around.  */
   if (odd > gad->max)
@@ -3212,6 +4884,11 @@ _frvfdpic_compute_got_alloc_data (struct _frvfdpic_dynamic_got_alloc_data *gad,
   if (gad->cur == gad->max)
     gad->cur = gad->min;
 
+  /* Ditto for _frvfdpic_get_tlsdesc_entry().  */
+  gad->tcur = gad->max;
+  if (gad->tcur == gad->tmax)
+    gad->tcur = gad->tmin;
+
   return odd;
 }
 
@@ -3222,7 +4899,7 @@ inline static bfd_signed_vma
 _frvfdpic_get_got_entry (struct _frvfdpic_dynamic_got_alloc_data *gad)
 {
   bfd_signed_vma ret;
-  
+
   if (gad->odd)
     {
       /* If there was an odd word left behind, use it.  */
@@ -3257,6 +4934,24 @@ _frvfdpic_get_fd_entry (struct _frvfdpic_dynamic_got_alloc_data *gad)
   return gad->fdcur -= 8;
 }
 
+/* Compute the location of the next TLS descriptor entry in the GOT,
+   given the allocation data for a range.  */
+inline static bfd_signed_vma
+_frvfdpic_get_tlsdesc_entry (struct _frvfdpic_dynamic_got_alloc_data *gad)
+{
+  bfd_signed_vma ret;
+
+  ret = gad->tcur;
+
+  gad->tcur += 8;
+
+  /* If we're at the top of the region, wrap around to the bottom.  */
+  if (gad->tcur == gad->tmax)
+    gad->tcur = gad->tmin;
+
+  return ret;
+}
+
 /* Assign GOT offsets for every GOT entry and function descriptor.
    Doing everything in a single pass is tricky.  */
 
@@ -3301,7 +4996,36 @@ _frvfdpic_assign_got_entries (void **entryp, void *info_)
     }
   else if (entry->privfd)
     entry->fd_entry = _frvfdpic_get_fd_entry (&dinfo->gothilo);
-  
+
+  if (entry->tlsoff12)
+    entry->tlsoff_entry = _frvfdpic_get_got_entry (&dinfo->got12);
+  else if (entry->tlsofflos)
+    entry->tlsoff_entry = _frvfdpic_get_got_entry (&dinfo->gotlos);
+  else if (entry->tlsoffhilo)
+    entry->tlsoff_entry = _frvfdpic_get_got_entry (&dinfo->gothilo);
+
+  if (entry->tlsdesc12)
+    entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->got12);
+  else if (entry->tlsplt && dinfo->got12.tlsdplt)
+    {
+      dinfo->got12.tlsdplt -= 8;
+      entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->got12);
+    }
+  else if (entry->tlsdesclos)
+    entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->gotlos);
+  else if (entry->tlsplt && dinfo->gotlos.tlsdplt)
+    {
+      dinfo->gotlos.tlsdplt -= 8;
+      entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->gotlos);
+    }
+  else if (entry->tlsplt)
+    {
+      dinfo->gothilo.tlsdplt -= 8;
+      entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->gothilo);
+    }
+  else if (entry->tlsdeschilo)
+    entry->tlsdesc_entry = _frvfdpic_get_tlsdesc_entry (&dinfo->gothilo);
+
   return 1;
 }
 
@@ -3315,27 +5039,8 @@ _frvfdpic_assign_plt_entries (void **entryp, void *info_)
   struct frvfdpic_relocs_info *entry = *entryp;
   struct _frvfdpic_dynamic_got_plt_info *dinfo = info_;
 
-  /* If this symbol requires a local function descriptor, allocate
-     one.  */
-  if (entry->privfd && entry->fd_entry == 0)
-    {
-      if (dinfo->got12.fdplt)
-       {
-         entry->fd_entry = _frvfdpic_get_fd_entry (&dinfo->got12);
-         dinfo->got12.fdplt -= 8;
-       }
-      else if (dinfo->gotlos.fdplt)
-       {
-         entry->fd_entry = _frvfdpic_get_fd_entry (&dinfo->gotlos);
-         dinfo->gotlos.fdplt -= 8;
-       }
-      else
-       {
-         BFD_ASSERT (dinfo->gothilo.fdplt)
-         entry->fd_entry = _frvfdpic_get_fd_entry (&dinfo->gothilo);
-         dinfo->gothilo.fdplt -= 8;
-       }
-    }
+  if (entry->privfd)
+    BFD_ASSERT (entry->fd_entry);
 
   if (entry->plt)
     {
@@ -3370,9 +5075,82 @@ _frvfdpic_assign_plt_entries (void **entryp, void *info_)
          == FRVFDPIC_LZPLT_RESOLV_LOC)
        dinfo->g.lzplt += 4;
     }
-      
+
+  if (entry->tlsplt)
+    {
+      int size;
+
+      entry->tlsplt_entry
+       = frvfdpic_plt_section (dinfo->g.info)->size;
+
+      if (bfd_link_executable (dinfo->g.info)
+         && (entry->symndx != -1
+             || FRVFDPIC_SYM_LOCAL (dinfo->g.info, entry->d.h)))
+       {
+         if ((bfd_signed_vma)entry->addend >= -(1 << (16 - 1))
+             /* FIXME: here we use the size of the TLS section
+                as an upper bound for the value of the TLS
+                symbol, because we may not know the exact value
+                yet.  If we get it wrong, we'll just waste a
+                word in the PLT, and we should never get even
+                close to 32 KiB of TLS anyway.  */
+             && elf_hash_table (dinfo->g.info)->tls_sec
+             && (elf_hash_table (dinfo->g.info)->tls_sec->size
+                 + (bfd_signed_vma)(entry->addend) <= (1 << (16 - 1))))
+           size = 8;
+         else
+           size = 12;
+       }
+      else if (entry->tlsoff_entry)
+       {
+         if (entry->tlsoff_entry >= -(1 << (12 - 1))
+             && entry->tlsoff_entry < (1 << (12 - 1)))
+           size = 8;
+         else if (entry->tlsoff_entry >= -(1 << (16 - 1))
+                  && entry->tlsoff_entry < (1 << (16 - 1)))
+           size = 12;
+         else
+           size = 16;
+       }
+      else
+       {
+         BFD_ASSERT (entry->tlsdesc_entry);
+
+         if (entry->tlsdesc_entry >= -(1 << (12 - 1))
+             && entry->tlsdesc_entry < (1 << (12 - 1)))
+           size = 8;
+         else if (entry->tlsdesc_entry >= -(1 << (16 - 1))
+                  && entry->tlsdesc_entry < (1 << (16 - 1)))
+           size = 12;
+         else
+           size = 16;
+       }
+
+      frvfdpic_plt_section (dinfo->g.info)->size += size;
+    }
+
+  return 1;
+}
+
+/* Cancel out any effects of calling _frvfdpic_assign_got_entries and
+   _frvfdpic_assign_plt_entries.  */
+
+static int
+_frvfdpic_reset_got_plt_entries (void **entryp, void *ignore ATTRIBUTE_UNUSED)
+{
+  struct frvfdpic_relocs_info *entry = *entryp;
+
+  entry->got_entry = 0;
+  entry->fdgot_entry = 0;
+  entry->fd_entry = 0;
+  entry->plt_entry = (bfd_vma)-1;
+  entry->lzplt_entry = (bfd_vma)-1;
+  entry->tlsoff_entry = 0;
+  entry->tlsdesc_entry = 0;
+  entry->tlsplt_entry = (bfd_vma)-1;
+
   return 1;
-}  
+}
 
 /* Follow indirect and warning hash entries so that each got entry
    points to the final symbol definition.  P must point to a pointer
@@ -3430,100 +5208,92 @@ _frvfdpic_resolve_final_relocs_info (void **entryp, void *p)
   return 1;
 }
 
-/* Set the sizes of the dynamic sections.  */
+/* Compute the total size of the GOT, the PLT, the dynamic relocations
+   section and the rofixup section.  Assign locations for GOT and PLT
+   entries.  */
 
 static bfd_boolean
-elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
-                                     struct bfd_link_info *info)
+_frvfdpic_size_got_plt (bfd *output_bfd,
+                       struct _frvfdpic_dynamic_got_plt_info *gpinfop)
 {
-  bfd *dynobj;
-  asection *s;
-  struct _frvfdpic_dynamic_got_plt_info gpinfo;
   bfd_signed_vma odd;
-  bfd_vma limit;
-
-  dynobj = elf_hash_table (info)->dynobj;
-  BFD_ASSERT (dynobj != NULL);
-
-  if (elf_hash_table (info)->dynamic_sections_created)
-    {
-      /* Set the contents of the .interp section to the interpreter.  */
-      if (info->executable)
-       {
-         s = bfd_get_section_by_name (dynobj, ".interp");
-         BFD_ASSERT (s != NULL);
-         s->size = sizeof ELF_DYNAMIC_INTERPRETER;
-         s->contents = (bfd_byte *) ELF_DYNAMIC_INTERPRETER;
-       }
-    }
-
-  memset (&gpinfo, 0, sizeof (gpinfo));
-  gpinfo.g.info = info;
-
-  for (;;)
-    {
-      htab_t relocs = frvfdpic_relocs_info (info);
-
-      htab_traverse (relocs, _frvfdpic_resolve_final_relocs_info, &relocs);
-
-      if (relocs == frvfdpic_relocs_info (info))
-       break;
-    }
+  bfd_vma limit, tlslimit;
+  struct bfd_link_info *info = gpinfop->g.info;
+  bfd *dynobj = elf_hash_table (info)->dynobj;
 
-  htab_traverse (frvfdpic_relocs_info (info), _frvfdpic_count_got_plt_entries,
-                &gpinfo.g);
+  memcpy (frvfdpic_dynamic_got_plt_info (info), &gpinfop->g,
+         sizeof (gpinfop->g));
 
   odd = 12;
   /* Compute the total size taken by entries in the 12-bit and 16-bit
      ranges, to tell how many PLT function descriptors we can bring
      into the 12-bit range without causing the 16-bit range to
      overflow.  */
-  limit = odd + gpinfo.g.got12 + gpinfo.g.gotlos
-    + gpinfo.g.fd12 + gpinfo.g.fdlos;
+  limit = odd + gpinfop->g.got12 + gpinfop->g.gotlos
+    + gpinfop->g.fd12 + gpinfop->g.fdlos
+    + gpinfop->g.tlsd12 + gpinfop->g.tlsdlos;
   if (limit < (bfd_vma)1 << 16)
     limit = ((bfd_vma)1 << 16) - limit;
   else
     limit = 0;
-  if (gpinfo.g.fdplt < limit)
-    limit = gpinfo.g.fdplt;
+  if (gpinfop->g.fdplt < limit)
+    {
+      tlslimit = (limit - gpinfop->g.fdplt) & ~ (bfd_vma) 8;
+      limit = gpinfop->g.fdplt;
+    }
+  else
+    tlslimit = 0;
+  if (gpinfop->g.tlsdplt < tlslimit)
+    tlslimit = gpinfop->g.tlsdplt;
 
   /* Determine the ranges of GOT offsets that we can use for each
      range of addressing modes.  */
-  odd = _frvfdpic_compute_got_alloc_data (&gpinfo.got12,
+  odd = _frvfdpic_compute_got_alloc_data (&gpinfop->got12,
                                          0,
                                          odd,
                                          16,
-                                         gpinfo.g.got12,
-                                         gpinfo.g.fd12,
+                                         gpinfop->g.got12,
+                                         gpinfop->g.fd12,
                                          limit,
+                                         gpinfop->g.tlsd12,
+                                         tlslimit,
                                          (bfd_vma)1 << (12-1));
-  odd = _frvfdpic_compute_got_alloc_data (&gpinfo.gotlos,
-                                         gpinfo.got12.min,
+  odd = _frvfdpic_compute_got_alloc_data (&gpinfop->gotlos,
+                                         gpinfop->got12.tmin,
                                          odd,
-                                         gpinfo.got12.max,
-                                         gpinfo.g.gotlos,
-                                         gpinfo.g.fdlos,
-                                         gpinfo.g.fdplt - gpinfo.got12.fdplt,
+                                         gpinfop->got12.tmax,
+                                         gpinfop->g.gotlos,
+                                         gpinfop->g.fdlos,
+                                         gpinfop->g.fdplt
+                                         - gpinfop->got12.fdplt,
+                                         gpinfop->g.tlsdlos,
+                                         gpinfop->g.tlsdplt
+                                         - gpinfop->got12.tlsdplt,
                                          (bfd_vma)1 << (16-1));
-  odd = _frvfdpic_compute_got_alloc_data (&gpinfo.gothilo,
-                                         gpinfo.gotlos.min,
+  odd = _frvfdpic_compute_got_alloc_data (&gpinfop->gothilo,
+                                         gpinfop->gotlos.tmin,
                                          odd,
-                                         gpinfo.gotlos.max,
-                                         gpinfo.g.gothilo,
-                                         gpinfo.g.fdhilo,
-                                         gpinfo.g.fdplt - gpinfo.got12.fdplt
-                                         - gpinfo.gotlos.fdplt,
+                                         gpinfop->gotlos.tmax,
+                                         gpinfop->g.gothilo,
+                                         gpinfop->g.fdhilo,
+                                         gpinfop->g.fdplt
+                                         - gpinfop->got12.fdplt
+                                         - gpinfop->gotlos.fdplt,
+                                         gpinfop->g.tlsdhilo,
+                                         gpinfop->g.tlsdplt
+                                         - gpinfop->got12.tlsdplt
+                                         - gpinfop->gotlos.tlsdplt,
                                          (bfd_vma)1 << (32-1));
 
   /* Now assign (most) GOT offsets.  */
   htab_traverse (frvfdpic_relocs_info (info), _frvfdpic_assign_got_entries,
-                &gpinfo);
+                gpinfop);
 
-  frvfdpic_got_section (info)->size = gpinfo.gothilo.max
-    - gpinfo.gothilo.min
+  frvfdpic_got_section (info)->size = gpinfop->gothilo.tmax
+    - gpinfop->gothilo.tmin
     /* If an odd word is the last word of the GOT, we don't need this
        word to be part of the GOT.  */
-    - (odd + 4 == gpinfo.gothilo.max ? 4 : 0);
+    - (odd + 4 == gpinfop->gothilo.tmax ? 4 : 0);
   if (frvfdpic_got_section (info)->size == 0)
     frvfdpic_got_section (info)->flags |= SEC_EXCLUDE;
   else if (frvfdpic_got_section (info)->size == 12
@@ -3532,7 +5302,10 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
       frvfdpic_got_section (info)->flags |= SEC_EXCLUDE;
       frvfdpic_got_section (info)->size = 0;
     }
-  else
+  /* This will be non-NULL during relaxation.  The assumption is that
+     the size of one of these sections will never grow, only shrink,
+     so we can use the larger buffer we allocated before.  */
+  else if (frvfdpic_got_section (info)->contents == NULL)
     {
       frvfdpic_got_section (info)->contents =
        (bfd_byte *) bfd_zalloc (dynobj,
@@ -3540,18 +5313,18 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
       if (frvfdpic_got_section (info)->contents == NULL)
        return FALSE;
     }
-  
-  if (elf_hash_table (info)->dynamic_sections_created)
+
+  if (frvfdpic_gotrel_section (info))
     /* Subtract the number of lzplt entries, since those will generate
        relocations in the pltrel section.  */
     frvfdpic_gotrel_section (info)->size =
-      (gpinfo.g.relocs - gpinfo.g.lzplt / 8)
+      (gpinfop->g.relocs - gpinfop->g.lzplt / 8)
       * get_elf_backend_data (output_bfd)->s->sizeof_rel;
   else
-    BFD_ASSERT (gpinfo.g.relocs == 0);
+    BFD_ASSERT (gpinfop->g.relocs == 0);
   if (frvfdpic_gotrel_section (info)->size == 0)
     frvfdpic_gotrel_section (info)->flags |= SEC_EXCLUDE;
-  else
+  else if (frvfdpic_gotrel_section (info)->contents == NULL)
     {
       frvfdpic_gotrel_section (info)->contents =
        (bfd_byte *) bfd_zalloc (dynobj,
@@ -3560,10 +5333,10 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
        return FALSE;
     }
 
-  frvfdpic_gotfixup_section (info)->size = (gpinfo.g.fixups + 1) * 4;
+  frvfdpic_gotfixup_section (info)->size = (gpinfop->g.fixups + 1) * 4;
   if (frvfdpic_gotfixup_section (info)->size == 0)
     frvfdpic_gotfixup_section (info)->flags |= SEC_EXCLUDE;
-  else
+  else if (frvfdpic_gotfixup_section (info)->contents == NULL)
     {
       frvfdpic_gotfixup_section (info)->contents =
        (bfd_byte *) bfd_zalloc (dynobj,
@@ -3571,14 +5344,15 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
       if (frvfdpic_gotfixup_section (info)->contents == NULL)
        return FALSE;
     }
-  
-  if (elf_hash_table (info)->dynamic_sections_created)
+
+  if (frvfdpic_pltrel_section (info))
     {
       frvfdpic_pltrel_section (info)->size =
-       gpinfo.g.lzplt / 8 * get_elf_backend_data (output_bfd)->s->sizeof_rel;
+       gpinfop->g.lzplt / 8
+       * get_elf_backend_data (output_bfd)->s->sizeof_rel;
       if (frvfdpic_pltrel_section (info)->size == 0)
        frvfdpic_pltrel_section (info)->flags |= SEC_EXCLUDE;
-      else
+      else if (frvfdpic_pltrel_section (info)->contents == NULL)
        {
          frvfdpic_pltrel_section (info)->contents =
            (bfd_byte *) bfd_zalloc (dynobj,
@@ -3587,46 +5361,51 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
            return FALSE;
        }
     }
-  
+
   /* Add 4 bytes for every block of at most 65535 lazy PLT entries,
      such that there's room for the additional instruction needed to
      call the resolver.  Since _frvfdpic_assign_got_entries didn't
      account for them, our block size is 4 bytes smaller than the real
      block size.  */
-  if (elf_hash_table (info)->dynamic_sections_created)
+  if (frvfdpic_plt_section (info))
     {
-      frvfdpic_plt_section (info)->size = gpinfo.g.lzplt
-       + ((gpinfo.g.lzplt + (FRVFDPIC_LZPLT_BLOCK_SIZE - 4) - 8)
+      frvfdpic_plt_section (info)->size = gpinfop->g.lzplt
+       + ((gpinfop->g.lzplt + (FRVFDPIC_LZPLT_BLOCK_SIZE - 4) - 8)
           / (FRVFDPIC_LZPLT_BLOCK_SIZE - 4) * 4);
     }
 
   /* Reset it, such that _frvfdpic_assign_plt_entries() can use it to
      actually assign lazy PLT entries addresses.  */
-  gpinfo.g.lzplt = 0;
+  gpinfop->g.lzplt = 0;
 
   /* Save information that we're going to need to generate GOT and PLT
      entries.  */
-  frvfdpic_got_initial_offset (info) = -gpinfo.gothilo.min;
+  frvfdpic_got_initial_offset (info) = -gpinfop->gothilo.tmin;
 
   if (get_elf_backend_data (output_bfd)->want_got_sym)
     elf_hash_table (info)->hgot->root.u.def.value
-      += frvfdpic_got_initial_offset (info);
+      = frvfdpic_got_initial_offset (info);
 
-  if (elf_hash_table (info)->dynamic_sections_created)
+  if (frvfdpic_plt_section (info))
     frvfdpic_plt_initial_offset (info) =
       frvfdpic_plt_section (info)->size;
 
+  /* Allocate a ret statement at plt_initial_offset, to be used by
+     locally-resolved TLS descriptors.  */
+  if (gpinfop->g.tls_ret_refs)
+    frvfdpic_plt_section (info)->size += 4;
+
   htab_traverse (frvfdpic_relocs_info (info), _frvfdpic_assign_plt_entries,
-                &gpinfo);
+                gpinfop);
 
   /* Allocate the PLT section contents only after
      _frvfdpic_assign_plt_entries has a chance to add the size of the
      non-lazy PLT entries.  */
-  if (elf_hash_table (info)->dynamic_sections_created)
+  if (frvfdpic_plt_section (info))
     {
       if (frvfdpic_plt_section (info)->size == 0)
        frvfdpic_plt_section (info)->flags |= SEC_EXCLUDE;
-      else
+      else if (frvfdpic_plt_section (info)->contents == NULL)
        {
          frvfdpic_plt_section (info)->contents =
            (bfd_byte *) bfd_zalloc (dynobj,
@@ -3636,6 +5415,57 @@ elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
        }
     }
 
+  return TRUE;
+}
+
+/* Set the sizes of the dynamic sections.  */
+
+static bfd_boolean
+elf32_frvfdpic_size_dynamic_sections (bfd *output_bfd,
+                                     struct bfd_link_info *info)
+{
+  bfd *dynobj;
+  asection *s;
+  struct _frvfdpic_dynamic_got_plt_info gpinfo;
+
+  dynobj = elf_hash_table (info)->dynobj;
+  BFD_ASSERT (dynobj != NULL);
+
+  if (elf_hash_table (info)->dynamic_sections_created)
+    {
+      /* Set the contents of the .interp section to the interpreter.  */
+      if (bfd_link_executable (info) && !info->nointerp)
+       {
+         s = bfd_get_linker_section (dynobj, ".interp");
+         BFD_ASSERT (s != NULL);
+         s->size = sizeof ELF_DYNAMIC_INTERPRETER;
+         s->contents = (bfd_byte *) ELF_DYNAMIC_INTERPRETER;
+       }
+    }
+
+  memset (&gpinfo, 0, sizeof (gpinfo));
+  gpinfo.g.info = info;
+
+  for (;;)
+    {
+      htab_t relocs = frvfdpic_relocs_info (info);
+
+      htab_traverse (relocs, _frvfdpic_resolve_final_relocs_info, &relocs);
+
+      if (relocs == frvfdpic_relocs_info (info))
+       break;
+    }
+
+  htab_traverse (frvfdpic_relocs_info (info), _frvfdpic_count_got_plt_entries,
+                &gpinfo.g);
+
+  /* Allocate space to save the summary information, we're going to
+     use it if we're doing relaxations.  */
+  frvfdpic_dynamic_got_plt_info (info) = bfd_alloc (dynobj, sizeof (gpinfo.g));
+
+  if (!_frvfdpic_size_got_plt (output_bfd, &gpinfo))
+    return FALSE;
+
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       if (frvfdpic_got_section (info)->size)
@@ -3663,90 +5493,201 @@ static bfd_boolean
 elf32_frvfdpic_always_size_sections (bfd *output_bfd,
                                     struct bfd_link_info *info)
 {
-  if (!info->relocatable)
+  if (!bfd_link_relocatable (info)
+      && !bfd_elf_stack_segment_size (output_bfd, info,
+                                     "__stacksize", DEFAULT_STACK_SIZE))
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Check whether any of the relocations was optimized away, and
+   subtract it from the relocation or fixup count.  */
+static bfd_boolean
+_frvfdpic_check_discarded_relocs (bfd *abfd, asection *sec,
+                                 struct bfd_link_info *info,
+
+                                 bfd_boolean *changed)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  struct elf_link_hash_entry **sym_hashes;
+  Elf_Internal_Rela *rel, *erel;
+
+  if ((sec->flags & SEC_RELOC) == 0
+      || sec->reloc_count == 0)
+    return TRUE;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  sym_hashes = elf_sym_hashes (abfd);
+
+  rel = elf_section_data (sec)->relocs;
+
+  /* Now examine each relocation.  */
+  for (erel = rel + sec->reloc_count; rel < erel; rel++)
     {
       struct elf_link_hash_entry *h;
-      asection *sec;
+      unsigned long r_symndx;
+      struct frvfdpic_relocs_info *picrel;
+      struct _frvfdpic_dynamic_got_info *dinfo;
+
+      if (ELF32_R_TYPE (rel->r_info) != R_FRV_32
+         && ELF32_R_TYPE (rel->r_info) != R_FRV_FUNCDESC)
+       continue;
 
-      /* Force a PT_GNU_STACK segment to be created.  */
-      if (! elf_tdata (output_bfd)->stack_flags)
-       elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
+      if (_bfd_elf_section_offset (sec->output_section->owner,
+                                  info, sec, rel->r_offset)
+         != (bfd_vma)-1)
+       continue;
 
-      /* Define __stacksize if it's not defined yet.  */
-      h = elf_link_hash_lookup (elf_hash_table (info), "__stacksize",
-                               FALSE, FALSE, FALSE);
-      if (! h || h->root.type != bfd_link_hash_defined
-         || h->type != STT_OBJECT
-         || !h->def_regular)
+      r_symndx = ELF32_R_SYM (rel->r_info);
+      if (r_symndx < symtab_hdr->sh_info)
+       h = NULL;
+      else
        {
-         struct bfd_link_hash_entry *bh = NULL;
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *)h->root.u.i.link;
+       }
 
-         if (!(_bfd_generic_link_add_one_symbol
-               (info, output_bfd, "__stacksize",
-                BSF_GLOBAL, bfd_abs_section_ptr, DEFAULT_STACK_SIZE,
-                (const char *) NULL, FALSE,
-                get_elf_backend_data (output_bfd)->collect, &bh)))
-           return FALSE;
+      if (h != NULL)
+       picrel = frvfdpic_relocs_info_for_global (frvfdpic_relocs_info (info),
+                                                 abfd, h,
+                                                 rel->r_addend, NO_INSERT);
+      else
+       picrel = frvfdpic_relocs_info_for_local (frvfdpic_relocs_info (info),
+                                                abfd, r_symndx,
+                                                rel->r_addend, NO_INSERT);
 
-         h = (struct elf_link_hash_entry *) bh;
-         h->def_regular = 1;
-         h->type = STT_OBJECT;
-       }
+      if (! picrel)
+       return FALSE;
+
+      *changed = TRUE;
+      dinfo = frvfdpic_dynamic_got_plt_info (info);
+
+      _frvfdpic_count_relocs_fixups (picrel, dinfo, TRUE);
+      if (ELF32_R_TYPE (rel->r_info) == R_FRV_32)
+       picrel->relocs32--;
+      else /* we know (ELF32_R_TYPE (rel->r_info) == R_FRV_FUNCDESC) */
+       picrel->relocsfd--;
+      _frvfdpic_count_relocs_fixups (picrel, dinfo, FALSE);
+    }
 
-      /* Create a stack section, and set its alignment.  */
-      sec = bfd_make_section (output_bfd, ".stack");
+  return TRUE;
+}
+
+static bfd_boolean
+frvfdpic_elf_discard_info (bfd *ibfd,
+                          struct elf_reloc_cookie *cookie ATTRIBUTE_UNUSED,
+                          struct bfd_link_info *info)
+{
+  bfd_boolean changed = FALSE;
+  asection *s;
+  bfd *obfd = NULL;
+
+  /* Account for relaxation of .eh_frame section.  */
+  for (s = ibfd->sections; s; s = s->next)
+    if (s->sec_info_type == SEC_INFO_TYPE_EH_FRAME)
+      {
+       if (!_frvfdpic_check_discarded_relocs (ibfd, s, info, &changed))
+         return FALSE;
+       obfd = s->output_section->owner;
+      }
+
+  if (changed)
+    {
+      struct _frvfdpic_dynamic_got_plt_info gpinfo;
 
-      if (sec == NULL
-         || ! bfd_set_section_alignment (output_bfd, sec, 3))
+      memset (&gpinfo, 0, sizeof (gpinfo));
+      memcpy (&gpinfo.g, frvfdpic_dynamic_got_plt_info (info),
+             sizeof (gpinfo.g));
+
+      /* Clear GOT and PLT assignments.  */
+      htab_traverse (frvfdpic_relocs_info (info),
+                    _frvfdpic_reset_got_plt_entries,
+                    NULL);
+
+      if (!_frvfdpic_size_got_plt (obfd, &gpinfo))
        return FALSE;
     }
 
   return TRUE;
 }
 
+/* Look for opportunities to relax TLS relocations.  We can assume
+   we're linking the main executable or a static-tls library, since
+   otherwise we wouldn't have got here.  */
+
+static int
+_frvfdpic_relax_got_plt_entries (void **entryp, void *dinfo_)
+{
+  struct frvfdpic_relocs_info *entry = *entryp;
+  struct _frvfdpic_dynamic_got_info *dinfo = dinfo_;
+
+  _frvfdpic_relax_tls_entries (entry, dinfo, TRUE);
+
+  return 1;
+}
+
 static bfd_boolean
-elf32_frvfdpic_modify_segment_map (bfd *output_bfd,
-                                  struct bfd_link_info *info)
+elf32_frvfdpic_relax_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+                             struct bfd_link_info *info, bfd_boolean *again)
 {
-  struct elf_segment_map *m;
+  struct _frvfdpic_dynamic_got_plt_info gpinfo;
+
+  if (bfd_link_relocatable (info))
+    (*info->callbacks->einfo)
+      (_("%P%F: --relax and -r may not be used together\n"));
 
-  /* objcopy and strip preserve what's already there using
-     elf32_frvfdpic_copy_private_bfd_data ().  */
-  if (! info)
+  /* If we return early, we didn't change anything.  */
+  *again = FALSE;
+
+  /* We'll do our thing when requested to relax the GOT section.  */
+  if (sec != frvfdpic_got_section (info))
     return TRUE;
 
-  for (m = elf_tdata (output_bfd)->segment_map; m != NULL; m = m->next)
-    if (m->p_type == PT_GNU_STACK)
-      break;
+  /* We can only relax when linking the main executable or a library
+     that can't be dlopened.  */
+  if (! bfd_link_executable (info) && ! (info->flags & DF_STATIC_TLS))
+    return TRUE;
 
-  if (m)
-    {
-      asection *sec = bfd_get_section_by_name (output_bfd, ".stack");
-      struct elf_link_hash_entry *h;
+  /* If there isn't a TLS section for this binary, we can't do
+     anything about its TLS relocations (it probably doesn't have
+     any.  */
+  if (elf_hash_table (info)->tls_sec == NULL)
+    return TRUE;
 
-      if (sec)
-       {
-         /* Obtain the pointer to the __stacksize symbol.  */
-         h = elf_link_hash_lookup (elf_hash_table (info), "__stacksize",
-                                   FALSE, FALSE, FALSE);
-         while (h->root.type == bfd_link_hash_indirect
-                || h->root.type == bfd_link_hash_warning)
-           h = (struct elf_link_hash_entry *)h->root.u.i.link;
-         BFD_ASSERT (h->root.type == bfd_link_hash_defined);
+  memset (&gpinfo, 0, sizeof (gpinfo));
+  memcpy (&gpinfo.g, frvfdpic_dynamic_got_plt_info (info), sizeof (gpinfo.g));
 
-         /* Set the section size from the symbol value.  We
-            intentionally ignore the symbol section.  */
-         if (h->root.type == bfd_link_hash_defined)
-           sec->size = h->root.u.def.value;
-         else
-           sec->size = DEFAULT_STACK_SIZE;
+  /* Now look for opportunities to relax, adjusting the GOT usage
+     as needed.  */
+  htab_traverse (frvfdpic_relocs_info (info),
+                _frvfdpic_relax_got_plt_entries,
+                &gpinfo.g);
 
-         /* Add the stack section to the PT_GNU_STACK segment,
-            such that its size and alignment requirements make it
-            to the segment.  */
-         m->sections[m->count] = sec;
-         m->count++;
-       }
+  /* If we changed anything, reset and re-assign GOT and PLT entries.  */
+  if (memcmp (frvfdpic_dynamic_got_plt_info (info),
+             &gpinfo.g, sizeof (gpinfo.g)) != 0)
+    {
+      /* Clear GOT and PLT assignments.  */
+      htab_traverse (frvfdpic_relocs_info (info),
+                    _frvfdpic_reset_got_plt_entries,
+                    NULL);
+
+      /* The owner of the TLS section is the output bfd.  There should
+        be a better way to get to it.  */
+      if (!_frvfdpic_size_got_plt (elf_hash_table (info)->tls_sec->owner,
+                                  &gpinfo))
+       return FALSE;
+
+      /* Repeat until we don't make any further changes.  We could fail to
+        introduce changes in a round if, for example, the 12-bit range is
+        full, but we later release some space by getting rid of TLS
+        descriptors in it.  We have to repeat the whole process because
+        we might have changed the size of a section processed before this
+        one.  */
+      *again = TRUE;
     }
 
   return TRUE;
@@ -3771,6 +5712,10 @@ elf32_frvfdpic_finish_dynamic_sections (bfd *output_bfd,
 
   dynobj = elf_hash_table (info)->dynobj;
 
+  if (frvfdpic_dynamic_got_plt_info (info))
+    {
+      BFD_ASSERT (frvfdpic_dynamic_got_plt_info (info)->tls_ret_refs == 0);
+    }
   if (frvfdpic_got_section (info))
     {
       BFD_ASSERT (frvfdpic_gotrel_section (info)->size
@@ -3783,6 +5728,7 @@ elf32_frvfdpic_finish_dynamic_sections (bfd *output_bfd,
          bfd_vma got_value = hgot->root.u.def.value
            + hgot->root.u.def.section->output_section->vma
            + hgot->root.u.def.section->output_offset;
+         struct bfd_link_hash_entry *hend;
 
          _frvfdpic_add_rofixup (output_bfd, frvfdpic_gotfixup_section (info),
                                 got_value, 0);
@@ -3790,26 +5736,46 @@ elf32_frvfdpic_finish_dynamic_sections (bfd *output_bfd,
          if (frvfdpic_gotfixup_section (info)->size
              != (frvfdpic_gotfixup_section (info)->reloc_count * 4))
            {
-             (*_bfd_error_handler)
-               ("LINKER BUG: .rofixup section size mismatch");
+           error:
+             info->callbacks->einfo
+               ("LINKER BUG: .rofixup section size mismatch\n");
              return FALSE;
            }
+
+         hend = bfd_link_hash_lookup (info->hash, "__ROFIXUP_END__",
+                                      FALSE, FALSE, TRUE);
+         if (hend
+             && (hend->type == bfd_link_hash_defined
+                 || hend->type == bfd_link_hash_defweak)
+             && hend->u.def.section->output_section != NULL)
+           {
+             bfd_vma value =
+               frvfdpic_gotfixup_section (info)->output_section->vma
+               + frvfdpic_gotfixup_section (info)->output_offset
+               + frvfdpic_gotfixup_section (info)->size
+               - hend->u.def.section->output_section->vma
+               - hend->u.def.section->output_offset;
+             BFD_ASSERT (hend->u.def.value == value);
+             if (hend->u.def.value != value)
+               goto error;
+           }
        }
     }
-  if (elf_hash_table (info)->dynamic_sections_created)
+  if (frvfdpic_pltrel_section (info))
     {
       BFD_ASSERT (frvfdpic_pltrel_section (info)->size
                  == (frvfdpic_pltrel_section (info)->reloc_count
                      * sizeof (Elf32_External_Rel)));
     }
 
-  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       Elf32_External_Dyn * dyncon;
       Elf32_External_Dyn * dynconend;
 
+      sdyn = bfd_get_linker_section (dynobj, ".dynamic");
+
       BFD_ASSERT (sdyn != NULL);
 
       dyncon = (Elf32_External_Dyn *) sdyn->contents;
@@ -3865,7 +5831,7 @@ elf32_frvfdpic_adjust_dynamic_symbol
 
   /* Make sure we know what is going on here.  */
   BFD_ASSERT (dynobj != NULL
-             && (h->u.weakdef != NULL
+             && (h->is_weakalias
                  || (h->def_dynamic
                      && h->ref_regular
                      && !h->def_regular)));
@@ -3873,12 +5839,13 @@ elf32_frvfdpic_adjust_dynamic_symbol
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
      real definition first, and we can just use the same value.  */
-  if (h->u.weakdef != NULL)
+  if (h->is_weakalias)
     {
-      BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined
-                 || h->u.weakdef->root.type == bfd_link_hash_defweak);
-      h->root.u.def.section = h->u.weakdef->root.u.def.section;
-      h->root.u.def.value = h->u.weakdef->root.u.def.value;
+      struct elf_link_hash_entry *def = weakdef (h);
+      BFD_ASSERT (def->root.type == bfd_link_hash_defined);
+      h->root.u.def.section = def->root.u.def.section;
+      h->root.u.def.value = def->root.u.def.value;
+      return TRUE;
     }
 
   return TRUE;
@@ -4045,27 +6012,23 @@ frvfdpic_elf_encode_eh_address (bfd *abfd,
    Given infinite time and money... :-)  */
 
 static bfd_boolean
-elf32_frv_check_relocs (abfd, info, sec, relocs)
-     bfd *abfd;
-     struct bfd_link_info *info;
-     asection *sec;
-     const Elf_Internal_Rela *relocs;
+elf32_frv_check_relocs (bfd *abfd,
+                       struct bfd_link_info *info,
+                       asection *sec,
+                       const Elf_Internal_Rela *relocs)
 {
   Elf_Internal_Shdr *symtab_hdr;
-  struct elf_link_hash_entry **sym_hashes, **sym_hashes_end;
+  struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
   bfd *dynobj;
   struct frvfdpic_relocs_info *picrel;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
-  sym_hashes_end = sym_hashes + symtab_hdr->sh_size/sizeof(Elf32_External_Sym);
-  if (!elf_bad_symtab (abfd))
-    sym_hashes_end -= symtab_hdr->sh_info;
 
   dynobj = elf_hash_table (info)->dynobj;
   rel_end = relocs + sec->reloc_count;
@@ -4076,12 +6039,26 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
-        h = NULL;
+       h = NULL;
       else
-        h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+       {
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+       }
 
       switch (ELF32_R_TYPE (rel->r_info))
        {
+       case R_FRV_GETTLSOFF:
+       case R_FRV_TLSDESC_VALUE:
+       case R_FRV_GOTTLSDESC12:
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+       case R_FRV_GOTTLSOFF12:
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
+       case R_FRV_TLSOFF:
        case R_FRV_GOT12:
        case R_FRV_GOTHI:
        case R_FRV_GOTLO:
@@ -4096,6 +6073,10 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
        case R_FRV_FUNCDESC_GOTOFFLO:
        case R_FRV_FUNCDESC:
        case R_FRV_FUNCDESC_VALUE:
+       case R_FRV_TLSMOFF12:
+       case R_FRV_TLSMOFFHI:
+       case R_FRV_TLSMOFFLO:
+       case R_FRV_TLSMOFF:
          if (! IS_FDPIC (abfd))
            goto bad_reloc;
          /* Fall through.  */
@@ -4145,17 +6126,17 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
          picrel = NULL;
          break;
        }
-      
+
       switch (ELF32_R_TYPE (rel->r_info))
-        {
+       {
        case R_FRV_LABEL24:
          if (IS_FDPIC (abfd))
            picrel->call = 1;
          break;
-               
+
        case R_FRV_FUNCDESC_VALUE:
          picrel->relocsfdv++;
-         if (bfd_get_section_flags (abfd, sec) & SEC_ALLOC)
+         if (bfd_section_flags (sec) & SEC_ALLOC)
            picrel->relocs32--;
          /* Fall through.  */
 
@@ -4164,14 +6145,14 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
            break;
 
          picrel->sym = 1;
-         if (bfd_get_section_flags (abfd, sec) & SEC_ALLOC)
+         if (bfd_section_flags (sec) & SEC_ALLOC)
            picrel->relocs32++;
          break;
-           
+
        case R_FRV_GOT12:
          picrel->got12 = 1;
          break;
-           
+
        case R_FRV_GOTHI:
        case R_FRV_GOTLO:
          picrel->gothilo = 1;
@@ -4180,45 +6161,84 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
        case R_FRV_FUNCDESC_GOT12:
          picrel->fdgot12 = 1;
          break;
-           
+
        case R_FRV_FUNCDESC_GOTHI:
        case R_FRV_FUNCDESC_GOTLO:
          picrel->fdgothilo = 1;
          break;
-           
+
        case R_FRV_GOTOFF12:
        case R_FRV_GOTOFFHI:
        case R_FRV_GOTOFFLO:
          picrel->gotoff = 1;
          break;
-           
+
        case R_FRV_FUNCDESC_GOTOFF12:
          picrel->fdgoff12 = 1;
          break;
-           
+
        case R_FRV_FUNCDESC_GOTOFFHI:
        case R_FRV_FUNCDESC_GOTOFFLO:
          picrel->fdgoffhilo = 1;
          break;
-           
+
        case R_FRV_FUNCDESC:
          picrel->fd = 1;
          picrel->relocsfd++;
          break;
-         
-        /* This relocation describes the C++ object vtable hierarchy.
-           Reconstruct it for later use during GC.  */
-        case R_FRV_GNU_VTINHERIT:
-          if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
-            return FALSE;
-          break;
-
-        /* This relocation describes which C++ vtable entries are actually
-           used.  Record for later use during GC.  */
-        case R_FRV_GNU_VTENTRY:
-          if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
-            return FALSE;
-          break;
+
+       case R_FRV_GETTLSOFF:
+         picrel->tlsplt = 1;
+         break;
+
+       case R_FRV_TLSDESC_VALUE:
+         picrel->relocstlsd++;
+         goto bad_reloc;
+
+       case R_FRV_GOTTLSDESC12:
+         picrel->tlsdesc12 = 1;
+         break;
+
+       case R_FRV_GOTTLSDESCHI:
+       case R_FRV_GOTTLSDESCLO:
+         picrel->tlsdeschilo = 1;
+         break;
+
+       case R_FRV_TLSMOFF12:
+       case R_FRV_TLSMOFFHI:
+       case R_FRV_TLSMOFFLO:
+       case R_FRV_TLSMOFF:
+         break;
+
+       case R_FRV_GOTTLSOFF12:
+         picrel->tlsoff12 = 1;
+         info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_FRV_GOTTLSOFFHI:
+       case R_FRV_GOTTLSOFFLO:
+         picrel->tlsoffhilo = 1;
+         info->flags |= DF_STATIC_TLS;
+         break;
+
+       case R_FRV_TLSOFF:
+         picrel->relocstlsoff++;
+         info->flags |= DF_STATIC_TLS;
+         goto bad_reloc;
+
+       /* This relocation describes the C++ object vtable hierarchy.
+          Reconstruct it for later use during GC.  */
+       case R_FRV_GNU_VTINHERIT:
+         if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset))
+           return FALSE;
+         break;
+
+       /* This relocation describes which C++ vtable entries are actually
+          used.  Record for later use during GC.  */
+       case R_FRV_GNU_VTENTRY:
+         if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_addend))
+           return FALSE;
+         break;
 
        case R_FRV_LABEL16:
        case R_FRV_LO16:
@@ -4228,15 +6248,18 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
        case R_FRV_GPREL32:
        case R_FRV_GPRELHI:
        case R_FRV_GPRELLO:
+       case R_FRV_TLSDESC_RELAX:
+       case R_FRV_GETTLSOFF_RELAX:
+       case R_FRV_TLSOFF_RELAX:
          break;
 
        default:
        bad_reloc:
-         (*_bfd_error_handler)
-           (_("%B: unsupported relocation type %i"),
-            abfd, ELF32_R_TYPE (rel->r_info));
+         /* xgettext:c-format */
+         _bfd_error_handler (_("%pB: unsupported relocation type %#x"),
+                             abfd, (unsigned int) ELF32_R_TYPE (rel->r_info));
          return FALSE;
-        }
+       }
     }
 
   return TRUE;
@@ -4246,8 +6269,7 @@ elf32_frv_check_relocs (abfd, info, sec, relocs)
 /* Return the machine subcode from the ELF e_flags header.  */
 
 static int
-elf32_frv_machine (abfd)
-     bfd *abfd;
+elf32_frv_machine (bfd *abfd)
 {
   switch (elf_elfheader (abfd)->e_flags & EF_FRV_CPU_MASK)
     {
@@ -4268,8 +6290,7 @@ elf32_frv_machine (abfd)
 /* Set the right machine number for a FRV ELF file.  */
 
 static bfd_boolean
-elf32_frv_object_p (abfd)
-     bfd *abfd;
+elf32_frv_object_p (bfd *abfd)
 {
   bfd_default_set_arch_mach (abfd, bfd_arch_frv, elf32_frv_machine (abfd));
   return (((elf_elfheader (abfd)->e_flags & EF_FRV_FDPIC) != 0)
@@ -4279,34 +6300,13 @@ elf32_frv_object_p (abfd)
 /* Function to set the ELF flag bits.  */
 
 static bfd_boolean
-frv_elf_set_private_flags (abfd, flags)
-     bfd *abfd;
-     flagword flags;
+frv_elf_set_private_flags (bfd *abfd, flagword flags)
 {
   elf_elfheader (abfd)->e_flags = flags;
   elf_flags_init (abfd) = TRUE;
   return TRUE;
 }
 
-/* Copy backend specific data from one object module to another.  */
-
-static bfd_boolean
-frv_elf_copy_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
-{
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return TRUE;
-
-  BFD_ASSERT (!elf_flags_init (obfd)
-             || elf_elfheader (obfd)->e_flags == elf_elfheader (ibfd)->e_flags);
-
-  elf_elfheader (obfd)->e_flags = elf_elfheader (ibfd)->e_flags;
-  elf_flags_init (obfd) = TRUE;
-  return TRUE;
-}
-
 /* Return true if the architecture described by elf header flag
    EXTENSION is an extension of the architecture described by BASE.  */
 
@@ -4334,64 +6334,23 @@ frv_elf_arch_extension_p (flagword base, flagword extension)
   return FALSE;
 }
 
-static bfd_boolean
-elf32_frvfdpic_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
-{
-  unsigned i;
-
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return TRUE;
-
-  if (! frv_elf_copy_private_bfd_data (ibfd, obfd))
-    return FALSE;
-
-  if (! elf_tdata (ibfd) || ! elf_tdata (ibfd)->phdr
-      || ! elf_tdata (obfd) || ! elf_tdata (obfd)->phdr)
-    return TRUE;
-
-  /* Copy the stack size.  */
-  for (i = 0; i < elf_elfheader (ibfd)->e_phnum; i++)
-    if (elf_tdata (ibfd)->phdr[i].p_type == PT_GNU_STACK)
-      {
-       Elf_Internal_Phdr *iphdr = &elf_tdata (ibfd)->phdr[i];
-
-       for (i = 0; i < elf_elfheader (obfd)->e_phnum; i++)
-         if (elf_tdata (obfd)->phdr[i].p_type == PT_GNU_STACK)
-           {
-             memcpy (&elf_tdata (obfd)->phdr[i], iphdr, sizeof (*iphdr));
-
-             /* Rewrite the phdrs, since we're only called after they
-                were first written.  */
-             if (bfd_seek (obfd, (bfd_signed_vma) get_elf_backend_data (obfd)
-                           ->s->sizeof_ehdr, SEEK_SET) != 0
-                 || get_elf_backend_data (obfd)->s
-                 ->write_out_phdrs (obfd, elf_tdata (obfd)->phdr,
-                                    elf_elfheader (obfd)->e_phnum) != 0)
-               return FALSE;
-             break;
-           }
-
-       break;
-      }
-
-  return TRUE;
-}
-
 /* Merge backend specific data from an object file to the output
    object file when linking.  */
 
 static bfd_boolean
-frv_elf_merge_private_bfd_data (ibfd, obfd)
-     bfd *ibfd;
-     bfd *obfd;
+frv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 {
+  bfd *obfd = info->output_bfd;
   flagword old_flags, old_partial;
   flagword new_flags, new_partial;
   bfd_boolean error = FALSE;
   char new_opt[80];
   char old_opt[80];
 
+  /* FIXME: What should be checked when linking shared libraries?  */
+  if ((ibfd->flags & DYNAMIC) != 0)
+    return TRUE;
+
   new_opt[0] = old_opt[0] = '\0';
   new_flags = elf_elfheader (ibfd)->e_flags;
   old_flags = elf_elfheader (obfd)->e_flags;
@@ -4400,9 +6359,10 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
     new_flags &= ~EF_FRV_PIC;
 
 #ifdef DEBUG
-  (*_bfd_error_handler) ("old_flags = 0x%.8lx, new_flags = 0x%.8lx, init = %s, filename = %s",
-                        old_flags, new_flags, elf_flags_init (obfd) ? "yes" : "no",
-                        bfd_get_filename (ibfd));
+  _bfd_error_handler
+    ("old_flags = 0x%.8x, new_flags = 0x%.8x, init = %s, filename = %s",
+     old_flags, new_flags, elf_flags_init (obfd) ? "yes" : "no",
+     bfd_get_filename (ibfd));
 #endif
 
   if (!elf_flags_init (obfd))                  /* First call, no flags set.  */
@@ -4417,7 +6377,7 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
   else                                         /* Possibly incompatible flags.  */
     {
       /* Warn if different # of gprs are used.  Note, 0 means nothing is
-         said about the size of gprs.  */
+        said about the size of gprs.  */
       new_partial = (new_flags & EF_FRV_GPR_MASK);
       old_partial = (old_flags & EF_FRV_GPR_MASK);
       if (new_partial == old_partial)
@@ -4447,7 +6407,7 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
        }
 
       /* Warn if different # of fprs are used.  Note, 0 means nothing is
-         said about the size of fprs.  */
+        said about the size of fprs.  */
       new_partial = (new_flags & EF_FRV_FPR_MASK);
       old_partial = (old_flags & EF_FRV_FPR_MASK);
       if (new_partial == old_partial)
@@ -4479,7 +6439,7 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
        }
 
       /* Warn if different dword support was used.  Note, 0 means nothing is
-         said about the dword support.  */
+        said about the dword support.  */
       new_partial = (new_flags & EF_FRV_DWORD_MASK);
       old_partial = (old_flags & EF_FRV_DWORD_MASK);
       if (new_partial == old_partial)
@@ -4524,14 +6484,14 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
                   | (old_flags & new_flags & EF_FRV_NOPACK));
 
       /* We don't have to do anything if the pic flags are the same, or the new
-         module(s) were compiled with -mlibrary-pic.  */
+        module(s) were compiled with -mlibrary-pic.  */
       new_partial = (new_flags & EF_FRV_PIC_FLAGS);
       old_partial = (old_flags & EF_FRV_PIC_FLAGS);
       if ((new_partial == old_partial) || ((new_partial & EF_FRV_LIBPIC) != 0))
        ;
 
       /* If the old module(s) were compiled with -mlibrary-pic, copy in the pic
-         flags if any from the new module.  */
+        flags if any from the new module.  */
       else if ((old_partial & EF_FRV_LIBPIC) != 0)
        old_flags = (old_flags & ~ EF_FRV_PIC_FLAGS) | new_partial;
 
@@ -4540,7 +6500,7 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
        old_flags |= new_partial;
 
       /* One module was compiled for pic and the other was not, see if we have
-         had any relocations that are not pic-safe.  */
+        had any relocations that are not pic-safe.  */
       else
        {
          if ((old_flags & EF_FRV_NON_PIC_RELOCS) == 0)
@@ -4550,10 +6510,11 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
              old_flags &= ~ EF_FRV_PIC_FLAGS;
 #ifndef FRV_NO_PIC_ERROR
              error = TRUE;
-             (*_bfd_error_handler)
-               (_("%s: compiled with %s and linked with modules that use non-pic relocations"),
-                bfd_get_filename (ibfd),
-                (new_flags & EF_FRV_BIGPIC) ? "-fPIC" : "-fpic");
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: compiled with %s and linked with modules"
+                  " that use non-pic relocations"),
+                ibfd, (new_flags & EF_FRV_BIGPIC) ? "-fPIC" : "-fpic");
 #endif
            }
        }
@@ -4603,9 +6564,10 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
       if (new_opt[0])
        {
          error = TRUE;
-         (*_bfd_error_handler)
-           (_("%s: compiled with %s and linked with modules compiled with %s"),
-            bfd_get_filename (ibfd), new_opt, old_opt);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB: compiled with %s and linked with modules compiled with %s"),
+            ibfd, new_opt, old_opt);
        }
 
       /* Warn about any other mismatches */
@@ -4615,9 +6577,11 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
        {
          old_flags |= new_partial;
          error = TRUE;
-         (*_bfd_error_handler)
-           (_("%s: uses different unknown e_flags (0x%lx) fields than previous modules (0x%lx)"),
-            bfd_get_filename (ibfd), (long)new_partial, (long)old_partial);
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB: uses different unknown e_flags (%#x) fields"
+              " than previous modules (%#x)"),
+            ibfd, new_partial, old_partial);
        }
     }
 
@@ -4636,13 +6600,13 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
     {
       error = TRUE;
       if (IS_FDPIC (obfd))
-       (*_bfd_error_handler)
-         (_("%s: cannot link non-fdpic object file into fdpic executable"),
-          bfd_get_filename (ibfd));
+       _bfd_error_handler
+         (_("%pB: cannot link non-fdpic object file into fdpic executable"),
+          ibfd);
       else
-       (*_bfd_error_handler)
-         (_("%s: cannot link fdpic object file into non-fdpic executable"),
-          bfd_get_filename (ibfd));
+       _bfd_error_handler
+         (_("%pB: cannot link fdpic object file into non-fdpic executable"),
+          ibfd);
     }
 
   if (error)
@@ -4652,10 +6616,8 @@ frv_elf_merge_private_bfd_data (ibfd, obfd)
 }
 
 \f
-bfd_boolean
-frv_elf_print_private_bfd_data (abfd, ptr)
-     bfd *abfd;
-     PTR ptr;
+static bfd_boolean
+frv_elf_print_private_bfd_data (bfd *abfd, void * ptr)
 {
   FILE *file = (FILE *) ptr;
   flagword flags;
@@ -4666,7 +6628,7 @@ frv_elf_print_private_bfd_data (abfd, ptr)
   _bfd_elf_print_private_bfd_data (abfd, ptr);
 
   flags = elf_elfheader (abfd)->e_flags;
-  fprintf (file, _("private flags = 0x%lx:"), (long)flags);
+  fprintf (file, _("private flags = 0x%lx:"), (unsigned long) flags);
 
   switch (flags & EF_FRV_CPU_MASK)
     {
@@ -4723,7 +6685,7 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 
   if (flags & EF_FRV_FDPIC)
     fprintf (file, " -mfdpic");
-  
+
   if (flags & EF_FRV_NON_PIC_RELOCS)
     fprintf (file, " non-pic relocations");
 
@@ -4735,27 +6697,107 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 }
 
 \f
+/* Support for core dump NOTE sections.  */
+
+static bfd_boolean
+elf32_frv_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
+{
+  int offset;
+  unsigned int raw_size;
+
+  switch (note->descsz)
+    {
+      default:
+       return FALSE;
+
+      /* The Linux/FRV elf_prstatus struct is 268 bytes long.  The other
+        hardcoded offsets and sizes listed below (and contained within
+        this lexical block) refer to fields in the target's elf_prstatus
+        struct.  */
+      case 268:
+       /* `pr_cursig' is at offset 12.  */
+       elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
+
+       /* `pr_pid' is at offset 24.  */
+       elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
+
+       /* `pr_reg' is at offset 72.  */
+       offset = 72;
+
+       /* Most grok_prstatus implementations set `raw_size' to the size
+          of the pr_reg field.  For Linux/FRV, we set `raw_size' to be
+          the size of `pr_reg' plus the size of `pr_exec_fdpic_loadmap'
+          and `pr_interp_fdpic_loadmap', both of which (by design)
+          immediately follow `pr_reg'.  This will allow these fields to
+          be viewed by GDB as registers.
+
+          `pr_reg' is 184 bytes long.  `pr_exec_fdpic_loadmap' and
+          `pr_interp_fdpic_loadmap' are 4 bytes each.  */
+       raw_size = 184 + 4 + 4;
+
+       break;
+    }
+
+  /* Make a ".reg/999" section.  */
+  return _bfd_elfcore_make_pseudosection (abfd, ".reg", raw_size,
+                                         note->descpos + offset);
+}
+
+static bfd_boolean
+elf32_frv_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
+{
+  switch (note->descsz)
+    {
+      default:
+       return FALSE;
+
+      /* The Linux/FRV elf_prpsinfo struct is 124 bytes long.  */
+      case 124:
+
+       /* `pr_fname' is found at offset 28 and is 16 bytes long.  */
+       elf_tdata (abfd)->core->program
+         = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
+
+       /* `pr_psargs' is found at offset 44 and is 80 bytes long.  */
+       elf_tdata (abfd)->core->command
+         = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80);
+    }
+
+  /* Note that for some reason, a spurious space is tacked
+     onto the end of the args in some (at least one anyway)
+     implementations, so strip it off if it exists.  */
+
+  {
+    char *command = elf_tdata (abfd)->core->command;
+    int n = strlen (command);
+
+    if (0 < n && command[n - 1] == ' ')
+      command[n - 1] = '\0';
+  }
+
+  return TRUE;
+}
 #define ELF_ARCH               bfd_arch_frv
 #define ELF_MACHINE_CODE       EM_CYGNUS_FRV
 #define ELF_MAXPAGESIZE                0x1000
 
-#define TARGET_BIG_SYM          bfd_elf32_frv_vec
+#define TARGET_BIG_SYM         frv_elf32_vec
 #define TARGET_BIG_NAME                "elf32-frv"
 
 #define elf_info_to_howto                      frv_info_to_howto_rela
 #define elf_backend_relocate_section           elf32_frv_relocate_section
 #define elf_backend_gc_mark_hook               elf32_frv_gc_mark_hook
-#define elf_backend_gc_sweep_hook              elf32_frv_gc_sweep_hook
-#define elf_backend_check_relocs                elf32_frv_check_relocs
+#define elf_backend_check_relocs               elf32_frv_check_relocs
 #define elf_backend_object_p                   elf32_frv_object_p
-#define elf_backend_add_symbol_hook             elf32_frv_add_symbol_hook
+#define elf_backend_add_symbol_hook            elf32_frv_add_symbol_hook
 
+#define elf_backend_stack_align                        8
 #define elf_backend_can_gc_sections            1
 #define elf_backend_rela_normal                        1
 
 #define bfd_elf32_bfd_reloc_type_lookup                frv_reloc_type_lookup
+#define bfd_elf32_bfd_reloc_name_lookup        frv_reloc_name_lookup
 #define bfd_elf32_bfd_set_private_flags                frv_elf_set_private_flags
-#define bfd_elf32_bfd_copy_private_bfd_data    frv_elf_copy_private_bfd_data
 #define bfd_elf32_bfd_merge_private_bfd_data   frv_elf_merge_private_bfd_data
 #define bfd_elf32_bfd_print_private_bfd_data   frv_elf_print_private_bfd_data
 
@@ -4769,13 +6811,20 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 #define elf_backend_finish_dynamic_sections \
                elf32_frv_finish_dynamic_sections
 
+#define elf_backend_grok_prstatus      elf32_frv_grok_prstatus
+#define elf_backend_grok_psinfo                elf32_frv_grok_psinfo
+
+#define elf_backend_linux_prpsinfo32_ugid16    TRUE
+
 #include "elf32-target.h"
 
+#undef ELF_TARGET_ID
+#define ELF_TARGET_ID          FRV_ELF_DATA
 #undef ELF_MAXPAGESIZE
 #define ELF_MAXPAGESIZE                0x4000
 
 #undef TARGET_BIG_SYM
-#define TARGET_BIG_SYM          bfd_elf32_frvfdpic_vec
+#define TARGET_BIG_SYM         frv_elf32_fdpic_vec
 #undef TARGET_BIG_NAME
 #define TARGET_BIG_NAME                "elf32-frvfdpic"
 #undef elf32_bed
@@ -4790,12 +6839,6 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 #undef elf_backend_always_size_sections
 #define elf_backend_always_size_sections \
                elf32_frvfdpic_always_size_sections
-#undef elf_backend_modify_segment_map
-#define elf_backend_modify_segment_map \
-               elf32_frvfdpic_modify_segment_map
-#undef bfd_elf32_bfd_copy_private_bfd_data
-#define bfd_elf32_bfd_copy_private_bfd_data \
-               elf32_frvfdpic_copy_private_bfd_data
 
 #undef elf_backend_create_dynamic_sections
 #define elf_backend_create_dynamic_sections \
@@ -4806,6 +6849,9 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 #undef elf_backend_size_dynamic_sections
 #define elf_backend_size_dynamic_sections \
                elf32_frvfdpic_size_dynamic_sections
+#undef bfd_elf32_bfd_relax_section
+#define bfd_elf32_bfd_relax_section \
+  elf32_frvfdpic_relax_section
 #undef elf_backend_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_symbol \
                elf32_frvfdpic_finish_dynamic_symbol
@@ -4813,6 +6859,9 @@ frv_elf_print_private_bfd_data (abfd, ptr)
 #define elf_backend_finish_dynamic_sections \
                elf32_frvfdpic_finish_dynamic_sections
 
+#undef elf_backend_discard_info
+#define elf_backend_discard_info \
+               frvfdpic_elf_discard_info
 #undef elf_backend_can_make_relative_eh_frame
 #define elf_backend_can_make_relative_eh_frame \
                frvfdpic_elf_use_relative_eh_frame
@@ -4824,12 +6873,12 @@ frv_elf_print_private_bfd_data (abfd, ptr)
                frvfdpic_elf_encode_eh_address
 
 #undef elf_backend_may_use_rel_p
-#define elf_backend_may_use_rel_p       1
+#define elf_backend_may_use_rel_p      1
 #undef elf_backend_may_use_rela_p
-#define elf_backend_may_use_rela_p      1
+#define elf_backend_may_use_rela_p     1
 /* We use REL for dynamic relocations only.  */
 #undef elf_backend_default_use_rela_p
-#define elf_backend_default_use_rela_p  1
+#define elf_backend_default_use_rela_p 1
 
 #undef elf_backend_omit_section_dynsym
 #define elf_backend_omit_section_dynsym _frvfdpic_link_omit_section_dynsym
This page took 0.098027 seconds and 4 git commands to generate.