public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] Add new relocations for LoongArch.
@ 2022-07-21  1:37 liuzhensong
  2022-07-21  1:37 ` [PATCH v2 1/6] bfd: Add supported for LoongArch new relocations liuzhensong
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

This is the v2 version of patches to support new relocations for LoongArch.

The new reloc types docnments are on:
https://github.com/loongson/LoongArch-Documentation/pull/57/files

The testsuite status:

                === binutils Summary === 

# of expected passes            241
# of unsupported tests          6

                === gas Summary ===

# of expected passes            270
# of unsupported tests          7

                === ld Summary ===

# of expected passes            1457
# of expected failures          11
# of untested testcases         1
# of unsupported tests          154


The patch set changelog:

v1 -> v2
1. Rename some relocations.
2. Fix bug caused by a pointer point to global ifunc.
3. Fix bug in relocation R_LARCH_SOP_PUSH_GPREL.
3. Change some comments.
4. Add testcases.


liuzhensong (6):
  bfd: Add supported for LoongArch new relocations.
  LoongArch:opcodes: Add new reloc types.
  LoongArch: gas: Add new reloc types.
  LoongArch: Move ifunc info to rela.dyn from rela.plt.
  bfd: Delete R_LARCH_NONE from dyn info of LoongArch.
  LoongArch: Add testcases for new relocate types.

 bfd/bfd-in2.h                                 |   37 +
 bfd/elfnn-loongarch.c                         | 1554 ++++++++++++-----
 bfd/elfxx-loongarch.c                         | 1371 +++++++++++----
 bfd/elfxx-loongarch.h                         |    4 +
 bfd/libbfd.h                                  |   37 +
 bfd/reloc.c                                   |   82 +
 binutils/readelf.c                            |    2 +
 gas/config/loongarch-lex.h                    |    3 +
 gas/config/loongarch-parse.y                  |   72 +-
 gas/config/tc-loongarch.c                     |  140 +-
 gas/config/tc-loongarch.h                     |    7 +-
 gas/testsuite/gas/all/gas.exp                 |    4 +-
 gas/testsuite/gas/loongarch/jmp_op.d          |   82 +-
 gas/testsuite/gas/loongarch/jmp_op.s          |   38 +-
 gas/testsuite/gas/loongarch/macro_op.d        |  778 +--------
 gas/testsuite/gas/loongarch/macro_op.s        |   44 +-
 gas/testsuite/gas/loongarch/macro_op_32.d     |   55 +
 gas/testsuite/gas/loongarch/macro_op_32.s     |   16 +
 .../gas/loongarch/macro_op_large_abs.d        |   77 +
 .../gas/loongarch/macro_op_large_abs.s        |    9 +
 .../gas/loongarch/macro_op_large_pc.d         |   77 +
 .../gas/loongarch/macro_op_large_pc.s         |    9 +
 gas/testsuite/gas/loongarch/reloc.d           |  167 ++
 gas/testsuite/gas/loongarch/reloc.s           |  144 ++
 include/elf/loongarch.h                       |  139 ++
 ld/testsuite/ld-elf/eh5.d                     |    2 +-
 ld/testsuite/ld-elf/pr26936.d                 |    2 +-
 ld/testsuite/ld-elf/shared.exp                |    6 +-
 ld/testsuite/ld-loongarch-elf/attr-ifunc-4.c  |   23 +
 .../ld-loongarch-elf/attr-ifunc-4.out         |    1 +
 ld/testsuite/ld-loongarch-elf/disas-jirl-32.d |   18 +-
 ld/testsuite/ld-loongarch-elf/disas-jirl.d    |   19 +-
 ld/testsuite/ld-loongarch-elf/ifunc.exp       |   34 +
 ld/testsuite/ld-loongarch-elf/jmp_op.d        |   84 +-
 ld/testsuite/ld-loongarch-elf/jmp_op.s        |   42 +-
 .../ld-loongarch-elf/libnopic-global.s        |  113 ++
 ld/testsuite/ld-loongarch-elf/macro_op.d      |  883 ++--------
 ld/testsuite/ld-loongarch-elf/macro_op.s      |   57 +-
 ld/testsuite/ld-loongarch-elf/macro_op_32.d   |  768 +-------
 ld/testsuite/ld-loongarch-elf/macro_op_32.s   |   43 +-
 .../ld-loongarch-elf/nopic-global-so.rd       |    5 +
 .../ld-loongarch-elf/nopic-global-so.sd       |   10 +
 .../ld-loongarch-elf/nopic-global.out         |    1 +
 ld/testsuite/ld-loongarch-elf/nopic-global.s  |  373 ++++
 ld/testsuite/ld-loongarch-elf/nopic-global.sd |    5 +
 ld/testsuite/ld-loongarch-elf/nopic-global.xd |    3 +
 ld/testsuite/ld-loongarch-elf/nopic-local.out |    1 +
 ld/testsuite/ld-loongarch-elf/nopic-local.rd  |    0
 ld/testsuite/ld-loongarch-elf/nopic-local.s   |  383 ++++
 ld/testsuite/ld-loongarch-elf/nopic-local.sd  |    5 +
 ld/testsuite/ld-loongarch-elf/nopic-local.xd  |    3 +
 .../ld-loongarch-elf/nopic-weak-global-so.rd  |    5 +
 .../ld-loongarch-elf/nopic-weak-global-so.sd  |   10 +
 .../ld-loongarch-elf/nopic-weak-global.out    |    1 +
 .../ld-loongarch-elf/nopic-weak-global.s      |  374 ++++
 .../ld-loongarch-elf/nopic-weak-global.sd     |    5 +
 .../ld-loongarch-elf/nopic-weak-global.xd     |    3 +
 .../ld-loongarch-elf/nopic-weak-local.out     |    1 +
 .../ld-loongarch-elf/nopic-weak-local.rd      |    0
 .../ld-loongarch-elf/nopic-weak-local.s       |  383 ++++
 .../ld-loongarch-elf/nopic-weak-local.sd      |    5 +
 .../ld-loongarch-elf/nopic-weak-local.xd      |    3 +
 ld/testsuite/ld-loongarch-elf/pic.exp         |  202 +++
 ld/testsuite/ld-loongarch-elf/pic.ld          |   18 +
 opcodes/loongarch-opc.c                       |  412 +++--
 65 files changed, 5773 insertions(+), 3481 deletions(-)
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_32.d
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_32.s
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_large_abs.d
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_large_abs.s
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_large_pc.d
 create mode 100644 gas/testsuite/gas/loongarch/macro_op_large_pc.s
 create mode 100644 gas/testsuite/gas/loongarch/reloc.d
 create mode 100644 gas/testsuite/gas/loongarch/reloc.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/attr-ifunc-4.c
 create mode 100644 ld/testsuite/ld-loongarch-elf/attr-ifunc-4.out
 create mode 100644 ld/testsuite/ld-loongarch-elf/ifunc.exp
 create mode 100644 ld/testsuite/ld-loongarch-elf/libnopic-global.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global-so.rd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global-so.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global.out
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-global.xd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-local.out
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-local.rd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-local.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-local.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-local.xd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global-so.rd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global-so.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global.out
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-global.xd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-local.out
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-local.rd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-local.s
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-local.sd
 create mode 100644 ld/testsuite/ld-loongarch-elf/nopic-weak-local.xd
 create mode 100644 ld/testsuite/ld-loongarch-elf/pic.exp
 create mode 100644 ld/testsuite/ld-loongarch-elf/pic.ld

-- 
2.31.1


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

* [PATCH v2 1/6] bfd: Add supported for LoongArch new relocations.
  2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
@ 2022-07-21  1:37 ` liuzhensong
  2022-07-21  1:37 ` [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types liuzhensong
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

  Define new reloc types according to linker needs.

  include/elf/
    loongarch.h

  bfd/
    bfd-in2.h
    libbfd.h
    reloc.c
    elfnn-loongarch.c
    elfxx-loongarch.c
    elfxx-loongarch.h
---
 bfd/bfd-in2.h           |   36 ++
 bfd/elfnn-loongarch.c   | 1202 +++++++++++++++++++++-------------
 bfd/elfxx-loongarch.c   | 1354 +++++++++++++++++++++++++++++----------
 bfd/elfxx-loongarch.h   |    4 +
 bfd/libbfd.h            |   36 ++
 bfd/reloc.c             |   79 +++
 include/elf/loongarch.h |  136 ++++
 7 files changed, 2095 insertions(+), 752 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 4e1182e93d4..91e6ad76b9a 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -6274,6 +6274,42 @@ assembler and not (currently) written to any object files.  */
   BFD_RELOC_LARCH_SUB24,
   BFD_RELOC_LARCH_SUB32,
   BFD_RELOC_LARCH_SUB64,
+  BFD_RELOC_LARCH_B16,
+  BFD_RELOC_LARCH_B21,
+  BFD_RELOC_LARCH_B26,
+  BFD_RELOC_LARCH_ABS_HI20,
+  BFD_RELOC_LARCH_ABS_LO12,
+  BFD_RELOC_LARCH_ABS64_LO20,
+  BFD_RELOC_LARCH_ABS64_HI12,
+  BFD_RELOC_LARCH_PCALA_HI20,
+  BFD_RELOC_LARCH_PCALA_LO12,
+  BFD_RELOC_LARCH_PCALA64_LO20,
+  BFD_RELOC_LARCH_PCALA64_HI12,
+  BFD_RELOC_LARCH_GOT_PC_HI20,
+  BFD_RELOC_LARCH_GOT_PC_LO12,
+  BFD_RELOC_LARCH_GOT64_PC_LO20,
+  BFD_RELOC_LARCH_GOT64_PC_HI12,
+  BFD_RELOC_LARCH_GOT_HI20,
+  BFD_RELOC_LARCH_GOT_LO12,
+  BFD_RELOC_LARCH_GOT64_LO20,
+  BFD_RELOC_LARCH_GOT64_HI12,
+  BFD_RELOC_LARCH_TLS_LE_HI20,
+  BFD_RELOC_LARCH_TLS_LE_LO12,
+  BFD_RELOC_LARCH_TLS_LE64_LO20,
+  BFD_RELOC_LARCH_TLS_LE64_HI12,
+  BFD_RELOC_LARCH_TLS_IE_PC_HI20,
+  BFD_RELOC_LARCH_TLS_IE_PC_LO12,
+  BFD_RELOC_LARCH_TLS_IE64_PC_LO20,
+  BFD_RELOC_LARCH_TLS_IE64_PC_HI12,
+  BFD_RELOC_LARCH_TLS_IE_HI20,
+  BFD_RELOC_LARCH_TLS_IE_LO12,
+  BFD_RELOC_LARCH_TLS_IE64_LO20,
+  BFD_RELOC_LARCH_TLS_IE64_HI12,
+  BFD_RELOC_LARCH_TLS_LD_PC_HI20,
+  BFD_RELOC_LARCH_TLS_LD_HI20,
+  BFD_RELOC_LARCH_TLS_GD_PC_HI20,
+  BFD_RELOC_LARCH_TLS_GD_HI20,
+  BFD_RELOC_LARCH_RELAX,
   BFD_RELOC_UNUSED };
 
 typedef enum bfd_reloc_code_real bfd_reloc_code_real_type;
diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 5b44901b9e0..21710dcb3fb 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -122,14 +122,16 @@ struct loongarch_elf_link_hash_table
 
 #define elf_backend_plt_readonly 1
 
-#define elf_backend_want_plt_sym 0
+#define elf_backend_want_plt_sym 1
 #define elf_backend_plt_alignment 4
 #define elf_backend_can_gc_sections 1
+#define elf_backend_can_refcount 1
 #define elf_backend_want_got_sym 1
 
 #define elf_backend_got_header_size (GOT_ENTRY_SIZE * 1)
 
 #define elf_backend_want_dynrelro 1
+#define elf_backend_rela_normal 1
 
 /* Generate a PLT header.  */
 
@@ -607,9 +609,6 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       struct elf_link_hash_entry *h;
       Elf_Internal_Sym *isym = NULL;
 
-      int need_dynreloc;
-      int only_need_pcrel;
-
       r_symndx = ELFNN_R_SYM (rel->r_info);
       r_type = ELFNN_R_TYPE (rel->r_info);
 
@@ -622,7 +621,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < symtab_hdr->sh_info)
 	{
 	  /* A local symbol.  */
-	  isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx);
+	  isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd, r_symndx);
 	  if (isym == NULL)
 	    return false;
 
@@ -655,17 +654,20 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  if (htab->elf.dynobj == NULL)
 	    htab->elf.dynobj = abfd;
 
+	  /* Create 'irelifunc' in PIC object.  */
+	  if (bfd_link_pic (info)
+	      && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
+	    return false;
+	  /* If '.plt' not represent, create '.iplt' to deal with ifunc.  */
+	  else if (!htab->elf.splt
+		   && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
+	    return false;
 	  /* Create the ifunc sections, iplt and ipltgot, for static
 	     executables.  */
 	  if ((r_type == R_LARCH_64 || r_type == R_LARCH_32)
 	      && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
 	    return false;
 
-	  if (!htab->elf.splt
-	      && !_bfd_elf_create_ifunc_sections (htab->elf.dynobj, info))
-	    /* If '.plt' not represent, create '.iplt' to deal with ifunc.  */
-	    return false;
-
 	  if (h->plt.refcount < 0)
 	    h->plt.refcount = 0;
 	  h->plt.refcount++;
@@ -674,17 +676,27 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	  elf_tdata (info->output_bfd)->has_gnu_osabi |= elf_gnu_osabi_ifunc;
 	}
 
-      need_dynreloc = 0;
-      only_need_pcrel = 0;
+      int need_dynreloc = 0;
+      int only_need_pcrel = 0;
+
       switch (r_type)
 	{
+	case R_LARCH_GOT_PC_HI20:
+	case R_LARCH_GOT_HI20:
 	case R_LARCH_SOP_PUSH_GPREL:
+	  /* For la.global.  */
+	  if (h)
+	    h->pointer_equality_needed = 1;
 	  if (!loongarch_elf_record_tls_and_got_reference (abfd, info, h,
 							   r_symndx,
 							   GOT_NORMAL))
 	    return false;
 	  break;
 
+	case R_LARCH_TLS_LD_PC_HI20:
+	case R_LARCH_TLS_LD_HI20:
+	case R_LARCH_TLS_GD_PC_HI20:
+	case R_LARCH_TLS_GD_HI20:
 	case R_LARCH_SOP_PUSH_TLS_GD:
 	  if (!loongarch_elf_record_tls_and_got_reference (abfd, info, h,
 							   r_symndx,
@@ -692,6 +704,8 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    return false;
 	  break;
 
+	case R_LARCH_TLS_IE_PC_HI20:
+	case R_LARCH_TLS_IE_HI20:
 	case R_LARCH_SOP_PUSH_TLS_GOT:
 	  if (bfd_link_pic (info))
 	    /* May fail for lazy-bind.  */
@@ -703,6 +717,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    return false;
 	  break;
 
+	case R_LARCH_TLS_LE_HI20:
 	case R_LARCH_SOP_PUSH_TLS_TPREL:
 	  if (!bfd_link_executable (info))
 	    return false;
@@ -715,6 +730,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    return false;
 	  break;
 
+	case R_LARCH_ABS_HI20:
 	case R_LARCH_SOP_PUSH_ABSOLUTE:
 	  if (h != NULL)
 	    /* If this reloc is in a read-only section, we might
@@ -726,16 +742,45 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	    h->non_got_ref = 1;
 	  break;
 
-	case R_LARCH_SOP_PUSH_PCREL:
+	case R_LARCH_PCALA_HI20:
 	  if (h != NULL)
 	    {
 	      h->non_got_ref = 1;
+	      h->pointer_equality_needed = 1;
+	    }
+
+	  break;
+
+	case R_LARCH_B21:
+	case R_LARCH_B16:
+	case R_LARCH_B26:
+	  if (h != NULL)
+	    {
+	      h->needs_plt = 1;
+	      if (!bfd_link_pic (info))
+		h->non_got_ref = 1;
+
+	      /* We try to create PLT stub for all non-local function.  */
+	      if (h->plt.refcount < 0)
+		h->plt.refcount = 0;
+	      h->plt.refcount++;
+	    }
+
+	  break;
+
+	case R_LARCH_SOP_PUSH_PCREL:
+	  if (h != NULL)
+	    {
+	      if (!bfd_link_pic (info))
+		h->non_got_ref = 1;
 
 	      /* We try to create PLT stub for all non-local function.  */
 	      if (h->plt.refcount < 0)
 		h->plt.refcount = 0;
 	      h->plt.refcount++;
+	      h->pointer_equality_needed = 1;
 	    }
+
 	  break;
 
 	case R_LARCH_SOP_PUSH_PLT_PCREL:
@@ -762,6 +807,7 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_LARCH_JUMP_SLOT:
 	case R_LARCH_32:
 	case R_LARCH_64:
+
 	  need_dynreloc = 1;
 
 	  /* If resolved symbol is defined in this object,
@@ -774,9 +820,6 @@ loongarch_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	     Thus, only under pde, it needs pcrel only.  We discard it.  */
 	  only_need_pcrel = bfd_link_pde (info);
 
-	  if (h != NULL)
-	    h->non_got_ref = 1;
-
 	  if (h != NULL
 	      && (!bfd_link_pic (info)
 		  || h->type == STT_GNU_IFUNC))
@@ -899,9 +942,7 @@ loongarch_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 				     struct elf_link_hash_entry *h)
 {
   struct loongarch_elf_link_hash_table *htab;
-  struct loongarch_elf_link_hash_entry *eh;
   bfd *dynobj;
-  asection *s, *srel;
 
   htab = loongarch_elf_hash_table (info);
   BFD_ASSERT (htab != NULL);
@@ -951,73 +992,9 @@ loongarch_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       return true;
     }
 
-  /* This is a reference to a symbol defined by a dynamic object which
-     is not a function.  */
-
-  /* If we are creating a shared library, we must presume that the
-     only references to the symbol are via the global offset table.
-     For such cases we need not do anything here; the relocations will
-     be handled correctly by relocate_section.  */
-  if (bfd_link_dll (info))
-    return true;
-
-  /* If there are no references to this symbol that do not use the
-     GOT, we don't need to generate a copy reloc.  */
-  if (!h->non_got_ref)
-    return true;
-
-  /* If -z nocopyreloc was given, we won't generate them either.  */
-  if (info->nocopyreloc)
-    {
-      h->non_got_ref = 0;
-      return true;
-    }
-
-  /* If we don't find any dynamic relocs in read-only sections, then
-     we'll be keeping the dynamic relocs and avoiding the copy reloc.  */
-  if (!readonly_dynrelocs (h))
-    {
-      h->non_got_ref = 0;
-      return true;
-    }
-
-  /* We must allocate the symbol in our .dynbss section, which will
-     become part of the .bss section of the executable.  There will be
-     an entry for this symbol in the .dynsym section.  The dynamic
-     object will contain position independent code, so all references
-     from the dynamic object to this symbol will go through the global
-     offset table.  The dynamic linker will use the .dynsym entry to
-     determine the address it must put in the global offset table, so
-     both the dynamic object and the regular object will refer to the
-     same memory location for the variable.  */
-
-  /* We must generate a R_LARCH_COPY reloc to tell the dynamic linker
-     to copy the initial value out of the dynamic object and into the
-     runtime process image.  We need to remember the offset into the
-     .rel.bss section we are going to use.  */
-  eh = (struct loongarch_elf_link_hash_entry *) h;
-  if (eh->tls_type & ~GOT_NORMAL)
-    {
-      s = htab->sdyntdata;
-      srel = htab->elf.srelbss;
-    }
-  else if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
-    {
-      s = htab->elf.sdynrelro;
-      srel = htab->elf.sreldynrelro;
-    }
-  else
-    {
-      s = htab->elf.sdynbss;
-      srel = htab->elf.srelbss;
-    }
-  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
-    {
-      srel->size += sizeof (ElfNN_External_Rela);
-      h->needs_copy = 1;
-    }
-
-  return _bfd_elf_adjust_dynamic_copy (info, h, s);
+  /* R_LARCH_COPY is not adept glibc, not to generate.  */
+  /* Can not print anything, because make check ld.  */
+  return true;
 }
 
 /* Allocate space in .plt, .got and associated reloc sections for
@@ -1039,6 +1016,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
   info = (struct bfd_link_info *) inf;
   htab = loongarch_elf_hash_table (info);
+  bool dyn = htab->elf.dynamic_sections_created;
   BFD_ASSERT (htab != NULL);
 
   do
@@ -1052,9 +1030,12 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
       if (htab->elf.splt)
 	{
-	  if (h->dynindx == -1 && !h->forced_local
-	      && !bfd_elf_link_record_dynamic_symbol (info, h))
-	    return false;
+	  if (h->dynindx == -1 && !h->forced_local && dyn
+	      && h->root.type == bfd_link_hash_undefweak)
+	    {
+	      if (!bfd_elf_link_record_dynamic_symbol (info, h))
+		return false;
+	    }
 
 	  if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, bfd_link_pic (info), h)
 	      && h->type != STT_GNU_IFUNC)
@@ -1090,7 +1071,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 	 location in the .plt.  This is required to make function
 	 pointers compare as equal between the normal executable and
 	 the shared library.  */
-      if (!bfd_link_pic(info)
+      if (!bfd_link_pic (info)
 	  && !h->def_regular)
 	{
 	  h->root.u.def.section = plt;
@@ -1107,51 +1088,68 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (0 < h->got.refcount)
     {
       asection *s;
-      bool dyn;
       int tls_type = loongarch_elf_hash_entry (h)->tls_type;
 
       /* Make sure this symbol is output as a dynamic symbol.
 	 Undefined weak syms won't yet be marked as dynamic.  */
-      if (h->dynindx == -1 && !h->forced_local)
+      if (h->dynindx == -1 && !h->forced_local && dyn
+	  && h->root.type == bfd_link_hash_undefweak)
 	{
-	  if (SYMBOL_REFERENCES_LOCAL (info, h)
-	      && (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-	      && h->start_stop)
-	    {
-	      /* The pr21964-4. do nothing.  */
-	    }
-	  else
-	    {
-	      if( !bfd_elf_link_record_dynamic_symbol (info, h))
-		return false;
-	    }
+	  if (!bfd_elf_link_record_dynamic_symbol (info, h))
+	    return false;
 	}
 
       s = htab->elf.sgot;
       h->got.offset = s->size;
-      dyn = htab->elf.dynamic_sections_created;
       if (tls_type & (GOT_TLS_GD | GOT_TLS_IE))
 	{
 	  /* TLS_GD needs two dynamic relocs and two GOT slots.  */
 	  if (tls_type & GOT_TLS_GD)
 	    {
 	      s->size += 2 * GOT_ENTRY_SIZE;
-	      htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
+	      if (bfd_link_executable (info))
+		{
+		  /* Link exe and not defined local.  */
+		  if (!SYMBOL_REFERENCES_LOCAL (info, h))
+		    htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
+		}
+	      else
+		{
+		  if (SYMBOL_REFERENCES_LOCAL (info, h))
+		    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+		  else
+		    htab->elf.srelgot->size += 2 * sizeof (ElfNN_External_Rela);
+		}
 	    }
 
 	  /* TLS_IE needs one dynamic reloc and one GOT slot.  */
 	  if (tls_type & GOT_TLS_IE)
 	    {
 	      s->size += GOT_ENTRY_SIZE;
-	      htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+
+	      if (bfd_link_executable (info))
+		{
+		  /* Link exe and not defined local.  */
+		  if (!SYMBOL_REFERENCES_LOCAL (info, h))
+		    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+		}
+	      else
+		{
+		  htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
+		}
 	    }
 	}
       else
 	{
 	  s->size += GOT_ENTRY_SIZE;
-	  if ((WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
-	       && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
-	      || h->type == STT_GNU_IFUNC)
+	  if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+	       || h->root.type != bfd_link_hash_undefweak)
+	      && (bfd_link_pic (info)
+		  || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info),
+						      h))
+	      && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
+	      /* Undefined weak symbol in static PIE resolves to 0 without
+		 any dynamic relocations.  */
 	    htab->elf.srelgot->size += sizeof (ElfNN_External_Rela);
 	}
     }
@@ -1161,7 +1159,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   if (h->dyn_relocs == NULL)
     return true;
 
-  if (SYMBOL_REFERENCES_LOCAL (info, h))
+  /* Extra dynamic relocate,
+   * R_LARCH_64
+   * R_LARCH_TLS_DTPRELNN
+   * R_LARCH_JUMP_SLOT
+   * R_LARCH_NN.  */
+
+  if (SYMBOL_CALLS_LOCAL (info, h))
     {
       struct elf_dyn_relocs **pp;
 
@@ -1178,13 +1182,20 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
   if (h->root.type == bfd_link_hash_undefweak)
     {
-      if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
+      if (UNDEFWEAK_NO_DYNAMIC_RELOC (info, h)
+	  || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+	  || (!bfd_link_pic (info) && h->non_got_ref))
 	h->dyn_relocs = NULL;
-      else if (h->dynindx == -1 && !h->forced_local
-	       /* Make sure this symbol is output as a dynamic symbol.
-		  Undefined weak syms won't yet be marked as dynamic.  */
-	       && !bfd_elf_link_record_dynamic_symbol (info, h))
-	return false;
+      else if (h->dynindx == -1 && !h->forced_local)
+	{
+	  /* Make sure this symbol is output as a dynamic symbol.
+	     Undefined weak syms won't yet be marked as dynamic.  */
+	  if (!bfd_elf_link_record_dynamic_symbol (info, h))
+	    return false;
+
+	  if (h->dynindx == -1)
+	    h->dyn_relocs = NULL;
+	}
     }
 
   for (p = h->dyn_relocs; p != NULL; p = p->next)
@@ -1200,8 +1211,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
    ifunc dynamic relocs.  */
 
 static bool
-elfNN_loongarch_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
-					  void *inf)
+elfNN_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 {
   struct bfd_link_info *info;
   /* An example of a bfd_link_hash_indirect symbol is versioned
@@ -1223,14 +1233,14 @@ elfNN_loongarch_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
 
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
      here if it is defined and referenced in a non-shared object.  */
-  if (h->type == STT_GNU_IFUNC
-      && h->def_regular)
+  if (h->type == STT_GNU_IFUNC && h->def_regular)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
 					       &h->dyn_relocs,
 					       PLT_ENTRY_SIZE,
 					       PLT_HEADER_SIZE,
 					       GOT_ENTRY_SIZE,
 					       false);
+
   return true;
 }
 
@@ -1238,7 +1248,7 @@ elfNN_loongarch_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
    ifunc dynamic relocs.  */
 
 static bool
-elfNN_loongarch_allocate_local_dynrelocs (void **slot, void *inf)
+elfNN_allocate_local_ifunc_dynrelocs (void **slot, void *inf)
 {
   struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) *slot;
 
@@ -1249,7 +1259,7 @@ elfNN_loongarch_allocate_local_dynrelocs (void **slot, void *inf)
       || h->root.type != bfd_link_hash_defined)
     abort ();
 
-  return elfNN_loongarch_allocate_ifunc_dynrelocs (h, inf);
+  return elfNN_allocate_ifunc_dynrelocs (h, inf);
 }
 
 /* Set DF_TEXTREL if we find any dynamic relocs that apply to
@@ -1367,16 +1377,21 @@ loongarch_elf_size_dynamic_sections (bfd *output_bfd,
 	  if (0 < *local_got)
 	    {
 	      *local_got = s->size;
-	      s->size += GOT_ENTRY_SIZE;
 
+	      /* TLS gd use two got.  */
 	      if (*local_tls_type & GOT_TLS_GD)
+		s->size += GOT_ENTRY_SIZE * 2;
+	      else
+		/* Normal got, tls ie/ld use one got.  */
 		s->size += GOT_ENTRY_SIZE;
 
-	      /* If R_LARCH_RELATIVE.  */
-	      if (bfd_link_pic (info)
-		  /* Or R_LARCH_TLS_DTPRELNN or R_LARCH_TLS_TPRELNN.  */
-		  || (*local_tls_type & (GOT_TLS_GD | GOT_TLS_IE)))
-		srel->size += sizeof (ElfNN_External_Rela);
+	      if (bfd_link_executable (info)
+		  && (*local_tls_type & (GOT_TLS_GD| GOT_TLS_IE)))
+		;/* Do nothing.  */
+	      else
+		{
+		  srel->size += sizeof (ElfNN_External_Rela);
+		}
 	    }
 	  else
 	    *local_got = MINUS_ONE;
@@ -1389,11 +1404,11 @@ loongarch_elf_size_dynamic_sections (bfd *output_bfd,
 
   /* Allocate global ifunc sym .plt and .got entries, and space for global
      ifunc sym dynamic relocs.  */
-  elf_link_hash_traverse (&htab->elf, elfNN_loongarch_allocate_ifunc_dynrelocs, info);
+  elf_link_hash_traverse (&htab->elf, elfNN_allocate_ifunc_dynrelocs, info);
 
   /* Allocate .plt and .got entries, and space for local ifunc symbols.  */
   htab_traverse (htab->loc_hash_table,
-		 (void *) elfNN_loongarch_allocate_local_dynrelocs, info);
+		 (void *) elfNN_allocate_local_ifunc_dynrelocs, info);
 
   /* Don't allocate .got.plt section if there are no PLT.  */
   if (htab->elf.sgotplt && htab->elf.sgotplt->size == GOTPLT_HEADER_SIZE
@@ -1542,10 +1557,13 @@ loongarch_top (int64_t *val)
 static void
 loongarch_elf_append_rela (bfd *abfd, asection *s, Elf_Internal_Rela *rel)
 {
+  BFD_ASSERT (s && s->contents);
   const struct elf_backend_data *bed;
   bfd_byte *loc;
 
   bed = get_elf_backend_data (abfd);
+  if (!(s->size > s->reloc_count * bed->s->sizeof_rela))
+    BFD_ASSERT (s->size > s->reloc_count * bed->s->sizeof_rela);
   loc = s->contents + (s->reloc_count++ * bed->s->sizeof_rela);
   bed->s->swap_reloca_out (abfd, rel, loc);
 }
@@ -1594,19 +1612,15 @@ loongarch_reloc_rewrite_imm_insn (const Elf_Internal_Rela *rel,
   return bfd_reloc_ok;
 }
 
-/* Emplace a static relocation.  */
-
 static bfd_reloc_status_type
 perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
 		    reloc_howto_type *howto, bfd_vma value,
 		    bfd *input_bfd, bfd_byte *contents)
 {
-  uint32_t insn1;
   int64_t opr1, opr2, opr3;
   bfd_reloc_status_type r = bfd_reloc_ok;
   int bits = bfd_get_reloc_size (howto) * 8;
 
-
   switch (ELFNN_R_TYPE (rel->r_info))
     {
     case R_LARCH_SOP_PUSH_PCREL:
@@ -1679,6 +1693,8 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
     case R_LARCH_SOP_POP_32_S_10_12:
     case R_LARCH_SOP_POP_32_S_10_16:
     case R_LARCH_SOP_POP_32_S_10_16_S2:
+    case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:
+    case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:
     case R_LARCH_SOP_POP_32_S_5_20:
     case R_LARCH_SOP_POP_32_U_10_12:
     case R_LARCH_SOP_POP_32_U:
@@ -1694,67 +1710,6 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
 					    contents, (bfd_vma)opr1);
       break;
 
-    case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:
-	{
-	  r = loongarch_pop (&opr1);
-	  if (r != bfd_reloc_ok)
-	    break;
-
-	  if ((opr1 & 0x3) != 0)
-	    {
-	      r = bfd_reloc_overflow;
-	      break;
-	    }
-
-	  uint32_t imm = opr1 >> howto->rightshift;
-	  if ((imm & (~0xfffffU)) && ((imm & (~0xfffffU)) != (~0xfffffU)))
-	    {
-	      r = bfd_reloc_overflow;
-	      break;
-	    }
-	  r = loongarch_check_offset (rel, input_section);
-	  if (r != bfd_reloc_ok)
-	    break;
-
-	  insn1 = bfd_get (bits, input_bfd, contents + rel->r_offset);
-	  insn1 = (insn1 & howto->src_mask)
-	    | ((imm & 0xffffU) << 10)
-	    | ((imm & 0x1f0000U) >> 16);
-	  bfd_put (bits, input_bfd, insn1, contents + rel->r_offset);
-	  break;
-	}
-
-    case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:
-      {
-	r = loongarch_pop (&opr1);
-	if (r != bfd_reloc_ok)
-	  break;
-
-	if ((opr1 & 0x3) != 0)
-	  {
-	    r = bfd_reloc_overflow;
-	    break;
-	  }
-
-	uint32_t imm = opr1 >> howto->rightshift;
-	if ((imm & (~0x1ffffffU)) && (imm & (~0x1ffffffU)) != (~0x1ffffffU))
-	  {
-	    r = bfd_reloc_overflow;
-	    break;
-	  }
-
-	r = loongarch_check_offset (rel, input_section);
-	if (r != bfd_reloc_ok)
-	  break;
-
-	insn1 = bfd_get (bits, input_bfd, contents + rel->r_offset);
-	insn1 = ((insn1 & howto->src_mask)
-		 | ((imm & 0xffffU) << 10)
-		 | ((imm & 0x3ff0000U) >> 16));
-	bfd_put (bits, input_bfd, insn1, contents + rel->r_offset);
-	break;
-      }
-
     case R_LARCH_TLS_DTPREL32:
     case R_LARCH_32:
     case R_LARCH_TLS_DTPREL64:
@@ -1792,6 +1747,55 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
       bfd_put (bits, input_bfd, opr1 - value, contents + rel->r_offset);
       break;
 
+    /* New reloc type.
+       R_LARCH_B16 ~ R_LARCH_TLS_GD_HI20.  */
+    case R_LARCH_B16:
+    case R_LARCH_B21:
+    case R_LARCH_B26:
+    case R_LARCH_ABS_HI20:
+    case R_LARCH_ABS_LO12:
+    case R_LARCH_ABS64_LO20:
+    case R_LARCH_ABS64_HI12:
+    case R_LARCH_PCALA_HI20:
+    case R_LARCH_PCALA_LO12:
+    case R_LARCH_PCALA64_LO20:
+    case R_LARCH_PCALA64_HI12:
+    case R_LARCH_GOT_PC_HI20:
+    case R_LARCH_GOT_PC_LO12:
+    case R_LARCH_GOT64_PC_LO20:
+    case R_LARCH_GOT64_PC_HI12:
+    case R_LARCH_GOT_HI20:
+    case R_LARCH_GOT_LO12:
+    case R_LARCH_GOT64_LO20:
+    case R_LARCH_GOT64_HI12:
+    case R_LARCH_TLS_LE_HI20:
+    case R_LARCH_TLS_LE_LO12:
+    case R_LARCH_TLS_LE64_LO20:
+    case R_LARCH_TLS_LE64_HI12:
+    case R_LARCH_TLS_IE_PC_HI20:
+    case R_LARCH_TLS_IE_PC_LO12:
+    case R_LARCH_TLS_IE64_PC_LO20:
+    case R_LARCH_TLS_IE64_PC_HI12:
+    case R_LARCH_TLS_IE_HI20:
+    case R_LARCH_TLS_IE_LO12:
+    case R_LARCH_TLS_IE64_LO20:
+    case R_LARCH_TLS_IE64_HI12:
+    case R_LARCH_TLS_LD_PC_HI20:
+    case R_LARCH_TLS_LD_HI20:
+    case R_LARCH_TLS_GD_PC_HI20:
+    case R_LARCH_TLS_GD_HI20:
+      r = loongarch_check_offset (rel, input_section);
+      if (r != bfd_reloc_ok)
+	break;
+
+      r = loongarch_reloc_rewrite_imm_insn (rel, input_section,
+					    howto, input_bfd,
+					    contents, value);
+      break;
+
+    case R_LARCH_RELAX:
+      break;
+
     default:
       r = bfd_reloc_notsupported;
     }
@@ -1900,7 +1904,6 @@ loongarch_dump_reloc_record (void (*p) (const char *fmt, ...))
      "-- Record dump end --\n\n");
 }
 
-
 static bool
 loongarch_reloc_is_fatal (struct bfd_link_info *info,
 			  bfd *input_bfd,
@@ -1945,8 +1948,27 @@ loongarch_reloc_is_fatal (struct bfd_link_info *info,
   return fatal;
 }
 
-
-
+#define RELOCATE_CALC_PC32_HI20(relocation, pc) 	\
+  ({							\
+    bfd_vma lo = (relocation) & ((bfd_vma)0xfff);	\
+    pc = pc & (~(bfd_vma)0xfff);			\
+    if (lo > 0x7ff)					\
+      {							\
+	relocation += 0x1000;				\
+      } 						\
+    relocation &= ~(bfd_vma)0xfff;			\
+    relocation -= pc;					\
+  })
+
+#define RELOCATE_CALC_PC64_HI32(relocation, pc)  	\
+  ({							\
+    bfd_vma lo = (relocation) & ((bfd_vma)0xfff);	\
+    if (lo > 0x7ff)					\
+      { 						\
+	relocation -= 0x100000000;      		\
+      }  						\
+    relocation -= (pc & ~(bfd_vma)0xffffffff);  	\
+  })
 
 static int
 loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
@@ -1983,8 +2005,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
       bool is_ie, is_undefweak, unresolved_reloc, defined_local;
       bool resolved_local, resolved_dynly, resolved_to_const;
       char tls_type;
-      bfd_vma relocation;
-      bfd_vma off, ie_off;
+      bfd_vma relocation, off, ie_off;
       int i, j;
 
       howto = loongarch_elf_rtype_to_howto (input_bfd, r_type);
@@ -2018,9 +2039,12 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  resolved_local = true;
 	  resolved_dynly = false;
 	  resolved_to_const = false;
+
+	  /* Calc in funtion elf_link_input_bfd,
+	   * if #define elf_backend_rela_normal to 1.  */
 	  if (bfd_link_relocatable (info)
 	      && ELF_ST_TYPE (sym->st_info) == STT_SECTION)
-	    rel->r_addend += sec->output_offset;
+	    continue;
 	}
       else
 	{
@@ -2092,7 +2116,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  resolved_to_const = true;
 	}
 
-      /* The ifunc without reference does not generate plt.  */
+      /* The ifunc reference generate plt.  */
       if (h && h->type == STT_GNU_IFUNC && h->plt.offset != MINUS_ONE)
 	{
 	  defined_local = true;
@@ -2150,20 +2174,25 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 				     + h->root.u.def.section->output_section->vma
 				     + h->root.u.def.section->output_offset);
 
-		  /* Dynamic relocations are stored in
-		     1. .rela.ifunc section in PIC object.
-		     2. .rela.got section in dynamic executable.
-		     3. .rela.iplt section in static executable.  */
-		  if (bfd_link_pic (info))
-		    sreloc = htab->elf.irelifunc;
-		  else if (htab->elf.splt != NULL)
-		    sreloc = htab->elf.srelgot;
+		  if (htab->elf.splt != NULL)
+		    sreloc = htab->elf.srelplt;
 		  else
 		    sreloc = htab->elf.irelplt;
 		}
 	      else if (resolved_dynly)
 		{
-		  outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
+		  if (h->dynindx == -1)
+		    {
+		      if (h->root.type == bfd_link_hash_undefined)
+			(*info->callbacks->undefined_symbol)
+			  (info, name, input_bfd, input_section,
+			   rel->r_offset, true);
+
+		      outrel.r_info = ELFNN_R_INFO (0, r_type);
+		    }
+		  else
+		    outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
+
 		  outrel.r_addend = rel->r_addend;
 		}
 	      else
@@ -2172,7 +2201,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		  outrel.r_addend = relocation + rel->r_addend;
 		}
 
-	      if (unresolved_reloc)
+	      /* No alloc space of func allocate_dynrelocs.  */
+	      if (unresolved_reloc
+		  && !(h && (h->is_weakalias || !h->dyn_relocs)))
 		loongarch_elf_append_rela (output_bfd, sreloc, &outrel);
 	    }
 
@@ -2341,12 +2372,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	case R_LARCH_SOP_PUSH_PLT_PCREL:
 	  unresolved_reloc = false;
 
-	  if (resolved_to_const)
-	    {
-	      relocation += rel->r_addend;
-	      break;
-	    }
-	  else if (is_undefweak)
+	  if (is_undefweak)
 	    {
 	      i = 0, j = 0;
 	      relocation = 0;
@@ -2427,10 +2453,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	  if (h != NULL)
 	    {
-	      off = h->got.offset;
+	      off = h->got.offset & (~1);
 
-	      if (off == MINUS_ONE
-		  && h->type != STT_GNU_IFUNC)
+	      if (h->got.offset == MINUS_ONE && h->type != STT_GNU_IFUNC)
 		{
 		  fatal = (loongarch_reloc_is_fatal
 			   (info, input_bfd, input_section, rel, howto,
@@ -2441,8 +2466,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	      /* Hidden symbol not has .got entry, only .got.plt entry
 		 so gprel is (plt - got).  */
-	      if (off == MINUS_ONE
-		  && h->type == STT_GNU_IFUNC)
+	      if (h->got.offset == MINUS_ONE && h->type == STT_GNU_IFUNC)
 		{
 		  if (h->plt.offset == (bfd_vma) -1)
 		    {
@@ -2455,79 +2479,76 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		  if (htab->elf.splt != NULL)
 		    {
 		      /* Section .plt header is 2 times of plt entry.  */
-		      off = sec_addr(htab->elf.sgotplt) + off
-			- sec_addr(htab->elf.sgot);
+		      off = sec_addr (htab->elf.sgotplt) + off
+			- sec_addr (htab->elf.sgot);
 		    }
 		  else
 		    {
 		      /* Section iplt not has plt header.  */
-		      off = sec_addr(htab->elf.igotplt) + off
-			- sec_addr(htab->elf.sgot);
+		      off = sec_addr (htab->elf.igotplt) + off
+			- sec_addr (htab->elf.sgot);
 		    }
 		}
 
-	      if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn, is_pic, h)
-		  || (is_pic && SYMBOL_REFERENCES_LOCAL (info, h)))
+	      if ((h->got.offset & 1) == 0)
 		{
-		  /* This is actually a static link, or it is a
-		     -Bsymbolic link and the symbol is defined
-		     locally, or the symbol was forced to be local
-		     because of a version file.  We must initialize
-		     this entry in the global offset table.  Since the
-		     offset must always be a multiple of the word size,
-		     we use the least significant bit to record whether
-		     we have initialized it already.
-
-		     When doing a dynamic link, we create a .rela.got
-		     relocation entry to initialize the value.  This
-		     is done in the finish_dynamic_symbol routine.  */
-
-		  if (resolved_dynly)
+		  if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn,
+							bfd_link_pic (info), h)
+		      && ((bfd_link_pic (info)
+			   && SYMBOL_REFERENCES_LOCAL (info, h))))
 		    {
-		      fatal = (loongarch_reloc_is_fatal
-			       (info, input_bfd, input_section, rel, howto,
-				bfd_reloc_dangerous, is_undefweak, name,
-				"Internal: here shouldn't dynamic."));
-		    }
+		      /* This is actually a static link, or it is a
+			 -Bsymbolic link and the symbol is defined
+			 locally, or the symbol was forced to be local
+			 because of a version file.  We must initialize
+			 this entry in the global offset table.  Since the
+			 offset must always be a multiple of the word size,
+			 we use the least significant bit to record whether
+			 we have initialized it already.
+
+			 When doing a dynamic link, we create a rela.got
+			 relocation entry to initialize the value.  This
+			 is done in the finish_dynamic_symbol routine.  */
+
+		      if (resolved_dynly)
+			{
+			  fatal = (loongarch_reloc_is_fatal
+				   (info, input_bfd, input_section, rel, howto,
+				    bfd_reloc_dangerous, is_undefweak, name,
+				    "Internal: here shouldn't dynamic."));
+			}
 
-		  if (!(defined_local || resolved_to_const))
-		    {
-		      fatal = (loongarch_reloc_is_fatal
-			       (info, input_bfd, input_section, rel, howto,
-				bfd_reloc_undefined, is_undefweak, name,
-				"Internal: "));
-		      break;
-		    }
+		      if (!(defined_local || resolved_to_const))
+			{
+			  fatal = (loongarch_reloc_is_fatal
+				   (info, input_bfd, input_section, rel, howto,
+				    bfd_reloc_undefined, is_undefweak, name,
+				    "Internal: "));
+			  break;
+			}
 
-		  if ((off & 1) != 0)
-		    off &= ~1;
-		  else
-		    {
-		      /* The pr21964-4. Create relocate entry.  */
-		      if (is_pic && h->start_stop)
+		      asection *s;
+		      Elf_Internal_Rela outrel;
+		      /* We need to generate a R_LARCH_RELATIVE reloc
+			 for the dynamic linker.  */
+		      s = htab->elf.srelgot;
+		      if (!s)
 			{
-			  asection *s;
-			  Elf_Internal_Rela outrel;
-			  /* We need to generate a R_LARCH_RELATIVE reloc
-			     for the dynamic linker.  */
-			  s = htab->elf.srelgot;
-			  if (!s)
-			    {
-			      fatal = loongarch_reloc_is_fatal (info, input_bfd,
-				    input_section, rel, howto,
-				    bfd_reloc_notsupported, is_undefweak, name,
-				    "Internal: '.rel.got' not represent");
-			      break;
-			    }
-
-			  outrel.r_offset = sec_addr (got) + off;
-			  outrel.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
-			  outrel.r_addend = relocation; /* Link-time addr.  */
-			  loongarch_elf_append_rela (output_bfd, s, &outrel);
+			  fatal = loongarch_reloc_is_fatal
+			    (info, input_bfd,
+			     input_section, rel, howto,
+			     bfd_reloc_notsupported, is_undefweak, name,
+			     "Internal: '.rel.got' not represent");
+			  break;
 			}
-		      bfd_put_NN (output_bfd, relocation, got->contents + off);
-		      h->got.offset |= 1;
+
+		      outrel.r_offset = sec_addr (got) + off;
+		      outrel.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
+		      outrel.r_addend = relocation; /* Link-time addr.  */
+		      loongarch_elf_append_rela (output_bfd, s, &outrel);
 		    }
+		  bfd_put_NN (output_bfd, relocation, got->contents + off);
+		  h->got.offset |= 1;
 		}
 	    }
 	  else
@@ -2541,9 +2562,9 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		  break;
 		}
 
-	      off = local_got_offsets[r_symndx];
+	      off = local_got_offsets[r_symndx] & (~1);
 
-	      if (off == MINUS_ONE)
+	      if (local_got_offsets[r_symndx] == MINUS_ONE)
 		{
 		  fatal = (loongarch_reloc_is_fatal
 			   (info, input_bfd, input_section, rel, howto,
@@ -2555,9 +2576,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	      /* The offset must always be a multiple of the word size.
 		 So, we can use the least significant bit to record
 		 whether we have already processed this entry.  */
-	      if ((off & 1) != 0)
-		off &= ~1;
-	      else
+	      if (local_got_offsets[r_symndx] == 0)
 		{
 		  if (is_pic)
 		    {
@@ -2586,160 +2605,519 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 		}
 	    }
 	  relocation = off;
+
 	  break;
 
 	case R_LARCH_SOP_PUSH_TLS_GOT:
 	case R_LARCH_SOP_PUSH_TLS_GD:
-	  if (r_type == R_LARCH_SOP_PUSH_TLS_GOT)
-	    is_ie = true;
+	  {
+	    unresolved_reloc = false;
+	    if (r_type == R_LARCH_SOP_PUSH_TLS_GOT)
+	      is_ie = true;
+
+	    bfd_vma got_off = 0;
+	    if (h != NULL)
+	      {
+		got_off = h->got.offset;
+		h->got.offset |= 1;
+	      }
+	    else
+	      {
+		got_off = local_got_offsets[r_symndx];
+		local_got_offsets[r_symndx] |= 1;
+	      }
+
+	    BFD_ASSERT (got_off != MINUS_ONE);
+
+	    ie_off = 0;
+	    tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
+	    if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_IE))
+	      ie_off = 2 * GOT_ENTRY_SIZE;
+
+	    if ((got_off & 1) == 0)
+	      {
+		Elf_Internal_Rela rela;
+		asection *srel = htab->elf.srelgot;
+		bfd_vma tls_block_off = 0;
+
+		if (SYMBOL_REFERENCES_LOCAL (info, h))
+		  {
+		    BFD_ASSERT (elf_hash_table (info)->tls_sec);
+		    tls_block_off = relocation
+			- elf_hash_table (info)->tls_sec->vma;
+		  }
+
+		if (tls_type & GOT_TLS_GD)
+		  {
+		    rela.r_offset = sec_addr (got) + got_off;
+		    rela.r_addend = 0;
+		    if (SYMBOL_REFERENCES_LOCAL (info, h))
+		      {
+			/* Local sym, used in exec, set module id 1.  */
+			if (bfd_link_executable (info))
+			  bfd_put_NN (output_bfd, 1, got->contents + got_off);
+			else
+			  {
+			    rela.r_info = ELFNN_R_INFO (0,
+							R_LARCH_TLS_DTPMODNN);
+			    loongarch_elf_append_rela (output_bfd, srel, &rela);
+			  }
+
+			bfd_put_NN (output_bfd, tls_block_off,
+				    got->contents + got_off + GOT_ENTRY_SIZE);
+		      }
+		    /* Dynamic resolved.  */
+		    else
+		      {
+			/* Dynamic relocate module id.  */
+			rela.r_info = ELFNN_R_INFO (h->dynindx,
+						    R_LARCH_TLS_DTPMODNN);
+			loongarch_elf_append_rela (output_bfd, srel, &rela);
+
+			/* Dynamic relocate offset of block.  */
+			rela.r_offset += GOT_ENTRY_SIZE;
+			rela.r_info = ELFNN_R_INFO (h->dynindx,
+						    R_LARCH_TLS_DTPRELNN);
+			loongarch_elf_append_rela (output_bfd, srel, &rela);
+		      }
+		  }
+		if (tls_type & GOT_TLS_IE)
+		  {
+		    rela.r_offset = sec_addr (got) + got_off + ie_off;
+		    if (SYMBOL_REFERENCES_LOCAL (info, h))
+		      {
+			/* Local sym, used in exec, set module id 1.  */
+			if (!bfd_link_executable (info))
+			  {
+			    rela.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
+			    rela.r_addend = tls_block_off;
+			    loongarch_elf_append_rela (output_bfd, srel, &rela);
+			  }
+
+			bfd_put_NN (output_bfd, tls_block_off,
+				    got->contents + got_off + ie_off);
+		      }
+		    /* Dynamic resolved.  */
+		    else
+		      {
+			/* Dynamic relocate offset of block.  */
+			rela.r_info = ELFNN_R_INFO (h->dynindx,
+						    R_LARCH_TLS_TPRELNN);
+			rela.r_addend = 0;
+			loongarch_elf_append_rela (output_bfd, srel, &rela);
+		      }
+		  }
+	      }
+
+	    relocation = (got_off & (~(bfd_vma)1)) + (is_ie ? ie_off : 0);
+	  }
+	  break;
+
+	/* New reloc types.  */
+	case R_LARCH_B21:
+	case R_LARCH_B26:
+	case R_LARCH_B16:
 	  unresolved_reloc = false;
+	  if (is_undefweak)
+	    {
+	      relocation = 0;
+	    }
 
-	  if (rel->r_addend != 0)
+	  if (resolved_local)
 	    {
-	      fatal = (loongarch_reloc_is_fatal
-		       (info, input_bfd, input_section, rel, howto,
-			bfd_reloc_notsupported, is_undefweak, name,
-			"Shouldn't be with r_addend."));
+	      relocation -= pc;
+	      relocation += rel->r_addend;
+	    }
+	  else if (resolved_dynly)
+	    {
+	      BFD_ASSERT (h
+			  && (h->plt.offset != MINUS_ONE
+			      || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+			  && rel->r_addend == 0);
+	      if (h && h->plt.offset == MINUS_ONE
+		  && ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+		{
+		  relocation -= pc;
+		  relocation += rel->r_addend;
+		}
+	      else
+		relocation = sec_addr (plt) + h->plt.offset - pc;
+	    }
+
+	  break;
+
+	case R_LARCH_ABS_HI20:
+	case R_LARCH_ABS_LO12:
+	case R_LARCH_ABS64_LO20:
+	case R_LARCH_ABS64_HI12:
+	  BFD_ASSERT (!is_pic);
+
+	  if (is_undefweak)
+	    {
+	      BFD_ASSERT (resolved_dynly);
+	      relocation = 0;
 	      break;
 	    }
+	  else if (resolved_to_const || resolved_local)
+	    {
+	      relocation += rel->r_addend;
+	    }
+	  else if (resolved_dynly)
+	    {
+	      unresolved_reloc = false;
+	      BFD_ASSERT ((plt && h && h->plt.offset != MINUS_ONE)
+			  && rel->r_addend == 0);
+	      relocation = sec_addr (plt) + h->plt.offset;
+	    }
+
+	  break;
+
+	case R_LARCH_PCALA_HI20:
+	  unresolved_reloc = false;
+	  if (h && h->plt.offset != MINUS_ONE)
+	    relocation = sec_addr (plt) + h->plt.offset;
+	  else
+	    relocation += rel->r_addend;
 
+	  RELOCATE_CALC_PC32_HI20 (relocation, pc);
+
+	  break;
+
+	case R_LARCH_PCALA_LO12:
+	  /* Not support if sym_addr in 2k page edge.
+	     pcalau12i pc_hi20 (sym_addr)
+	     ld.w/d pc_lo12 (sym_addr)
+	     ld.w/d pc_lo12 (sym_addr + x)
+	     ...
+	     can not calc correct address
+	     if sym_addr < 0x800 && sym_addr + x >= 0x800.  */
+
+	  if (h && h->plt.offset != MINUS_ONE)
+	    relocation = sec_addr (plt) + h->plt.offset;
+	  else
+	    relocation += rel->r_addend;
 
-	  if (resolved_to_const && is_undefweak && h->dynindx != -1)
 	    {
-	      /* What if undefweak? Let rtld make a decision.  */
-	      resolved_to_const = resolved_local = false;
-	      resolved_dynly = true;
+	      relocation &= 0xfff;
+	      /* Signed extend.  */
+	      relocation = (relocation ^ 0x800) - 0x800;
+
+	      /* For 2G jump, generate pcalau12i, jirl.  */
+	      /* If use jirl, turns to R_LARCH_B16.  */
+	      uint32_t insn = bfd_get (32, input_bfd, contents + rel->r_offset);
+	      if ((insn & 0x4c000000) == 0x4c000000)
+		{
+		  rel->r_info = ELFNN_R_INFO (r_symndx, R_LARCH_B16);
+		  howto = loongarch_elf_rtype_to_howto (input_bfd, R_LARCH_B16);
+		}
 	    }
+	  break;
 
-	  if (resolved_to_const)
+	case R_LARCH_PCALA64_LO20:
+	case R_LARCH_PCALA64_HI12:
+	  if (h && h->plt.offset != MINUS_ONE)
 	    {
-	      fatal = (loongarch_reloc_is_fatal
-		       (info, input_bfd, input_section, rel, howto,
-			bfd_reloc_notsupported, is_undefweak, name,
-			"Internal: Shouldn't be resolved to const."));
-	      break;
+	      BFD_ASSERT (rel->r_addend == 0);
+	      relocation = sec_addr (plt) + h->plt.offset;
+	    }
+	  else
+	    relocation += rel->r_addend;
+
+	  RELOCATE_CALC_PC64_HI32 (relocation, pc);
+
+	  break;
+
+	case R_LARCH_GOT_PC_HI20:
+	case R_LARCH_GOT_HI20:
+	  /* Calc got offset.  */
+	    {
+	      unresolved_reloc = false;
+	      BFD_ASSERT (rel->r_addend == 0);
+
+	      bfd_vma got_off = 0;
+	      if (h != NULL)
+		{
+		  /* GOT ref or ifunc.  */
+		  BFD_ASSERT (h->got.offset != MINUS_ONE
+			      || h->type == STT_GNU_IFUNC);
+
+		  got_off = h->got.offset  & (~(bfd_vma)1);
+		  /* Hidden symbol not has got entry,
+		   * only got.plt entry so it is (plt - got).  */
+		  if (h->got.offset == MINUS_ONE && h->type == STT_GNU_IFUNC)
+		    {
+		      bfd_vma idx;
+		      if (htab->elf.splt != NULL)
+			{
+			  idx = (h->plt.offset - PLT_HEADER_SIZE)
+			    / PLT_ENTRY_SIZE;
+			  got_off = sec_addr (htab->elf.sgotplt)
+			    + GOTPLT_HEADER_SIZE
+			    + (idx * GOT_ENTRY_SIZE)
+			    - sec_addr (htab->elf.sgot);
+			}
+		      else
+			{
+			  idx = h->plt.offset / PLT_ENTRY_SIZE;
+			  got_off = sec_addr (htab->elf.sgotplt)
+			    + (idx * GOT_ENTRY_SIZE)
+			    - sec_addr (htab->elf.sgot);
+			}
+		    }
+
+		  if ((h->got.offset & 1) == 0)
+		    {
+		      /* We need to generate a R_LARCH_RELATIVE reloc once
+		       * in loongarch_elf_finish_dynamic_symbol or now,
+		       * call finish_dyn && nopic
+		       * or !call finish_dyn && pic.  */
+		      if (!WILL_CALL_FINISH_DYNAMIC_SYMBOL (is_dyn,
+							    bfd_link_pic (info),
+							    h)
+			  && bfd_link_pic (info)
+			  && SYMBOL_REFERENCES_LOCAL (info, h))
+			{
+			  Elf_Internal_Rela rela;
+			  rela.r_offset = sec_addr (got) + got_off;
+			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
+			  rela.r_addend = relocation;
+			  loongarch_elf_append_rela (output_bfd,
+						     htab->elf.srelgot, &rela);
+			}
+		      h->got.offset |= 1;
+		    }
+		}
+	      else
+		{
+		  BFD_ASSERT (local_got_offsets
+			      && local_got_offsets[r_symndx] != MINUS_ONE);
+
+		  got_off = local_got_offsets[r_symndx] & (~(bfd_vma)1);
+		  if ((local_got_offsets[r_symndx] & 1) == 0)
+		    {
+		      if (bfd_link_pic (info))
+			{
+			  Elf_Internal_Rela rela;
+			  rela.r_offset = sec_addr (got) + got_off;
+			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
+			  rela.r_addend = relocation;
+			  loongarch_elf_append_rela (output_bfd,
+						     htab->elf.srelgot, &rela);
+			}
+		      local_got_offsets[r_symndx] |= 1;
+		    }
+		}
+
+	      bfd_put_NN (output_bfd, relocation, got->contents + got_off);
+
+	      relocation = got_off + sec_addr (got);
 	    }
 
+	  if (r_type == R_LARCH_GOT_PC_HI20)
+	    RELOCATE_CALC_PC32_HI20 (relocation, pc);
+
+	  break;
+
+	case R_LARCH_GOT_PC_LO12:
+	case R_LARCH_GOT64_PC_LO20:
+	case R_LARCH_GOT64_PC_HI12:
+	case R_LARCH_GOT_LO12:
+	case R_LARCH_GOT64_LO20:
+	case R_LARCH_GOT64_HI12:
+	    {
+	      unresolved_reloc = false;
+	      bfd_vma got_off;
+	      if (h)
+		got_off = h->got.offset & (~(bfd_vma)1);
+	      else
+		got_off = local_got_offsets[r_symndx] & (~(bfd_vma)1);
+
+	      if (h && h->got.offset == MINUS_ONE && h->type == STT_GNU_IFUNC)
+		{
+		  bfd_vma idx;
+		  if (htab->elf.splt != NULL)
+		    idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
+		  else
+		    idx = h->plt.offset / PLT_ENTRY_SIZE;
+
+		  got_off = sec_addr (htab->elf.sgotplt)
+		    + GOTPLT_HEADER_SIZE
+		    + (idx * GOT_ENTRY_SIZE)
+		    - sec_addr (htab->elf.sgot);
+		}
+	      relocation = got_off + sec_addr (got);
+	    }
+
+	  if (r_type == R_LARCH_GOT_PC_LO12)
+	    relocation &= (bfd_vma)0xfff;
+	  else if (r_type == R_LARCH_GOT64_PC_LO20
+		   || r_type == R_LARCH_GOT64_PC_HI12)
+	    RELOCATE_CALC_PC64_HI32 (relocation, pc);
+
+	  break;
+
+	case R_LARCH_TLS_LE_HI20:
+	case R_LARCH_TLS_LE_LO12:
+	case R_LARCH_TLS_LE64_LO20:
+	case R_LARCH_TLS_LE64_HI12:
+	  BFD_ASSERT (resolved_local && elf_hash_table (info)->tls_sec);
+
+	  relocation -= elf_hash_table (info)->tls_sec->vma;
+	  break;
+
+	/* TLS IE LD/GD process separately is troublesome.
+	   When a symbol is both ie and LD/GD, h->got.off |= 1
+	   make only one type be relocated.  We must use
+	   h->got.offset |= 1 and h->got.offset |= 2
+	   diff IE and LD/GD.  And all (got_off & (~(bfd_vma)1))
+	   (IE LD/GD and reusable GOT reloc) must change to
+	   (got_off & (~(bfd_vma)3)), beause we use lowest 2 bits
+	   as a tag.
+	   Now, LD and GD is both GOT_TLS_GD type, LD seems to
+	   can be omitted.  */
+	case R_LARCH_TLS_IE_PC_HI20:
+	case R_LARCH_TLS_IE_HI20:
+	case R_LARCH_TLS_LD_PC_HI20:
+	case R_LARCH_TLS_LD_HI20:
+	case R_LARCH_TLS_GD_PC_HI20:
+	case R_LARCH_TLS_GD_HI20:
+	  BFD_ASSERT (rel->r_addend == 0);
+	  unresolved_reloc = false;
+
+	  if (r_type == R_LARCH_TLS_IE_PC_HI20
+	      || r_type == R_LARCH_TLS_IE_HI20)
+	    is_ie = true;
+
+	  bfd_vma got_off = 0;
 	  if (h != NULL)
 	    {
-	      off = h->got.offset;
+	      got_off = h->got.offset;
 	      h->got.offset |= 1;
 	    }
 	  else
 	    {
-	      off = local_got_offsets[r_symndx];
+	      got_off = local_got_offsets[r_symndx];
 	      local_got_offsets[r_symndx] |= 1;
 	    }
 
-	  if (off == MINUS_ONE)
-	    {
-	      fatal = (loongarch_reloc_is_fatal
-		       (info, input_bfd, input_section, rel, howto,
-			bfd_reloc_notsupported, is_undefweak, name,
-			"Internal: TLS GOT entry doesn't represent."));
-	      break;
-	    }
-
-	  tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
+	  BFD_ASSERT (got_off != MINUS_ONE);
 
-	  /* If this symbol is referenced by both GD and IE TLS, the IE
-	     reference's GOT slot follows the GD reference's slots.  */
 	  ie_off = 0;
+	  tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
 	  if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_IE))
 	    ie_off = 2 * GOT_ENTRY_SIZE;
 
-	  if ((off & 1) != 0)
-	    off &= ~1;
-	  else
+	  if ((got_off & 1) == 0)
 	    {
+	      Elf_Internal_Rela rela;
+	      asection *relgot = htab->elf.srelgot;
 	      bfd_vma tls_block_off = 0;
-	      Elf_Internal_Rela outrel;
 
-	      if (resolved_local)
+	      if (SYMBOL_REFERENCES_LOCAL (info, h))
 		{
-		  if (!elf_hash_table (info)->tls_sec)
-		    {
-		      fatal = (loongarch_reloc_is_fatal
-			       (info, input_bfd, input_section, rel, howto,
-				bfd_reloc_notsupported, is_undefweak, name,
-				"Internal: TLS sec not represent."));
-		      break;
-		    }
-		  tls_block_off =
-		    relocation - elf_hash_table (info)->tls_sec->vma;
+		  BFD_ASSERT (elf_hash_table (info)->tls_sec);
+		  tls_block_off = relocation
+		      - elf_hash_table (info)->tls_sec->vma;
 		}
 
 	      if (tls_type & GOT_TLS_GD)
 		{
-		  outrel.r_offset = sec_addr (got) + off;
-		  outrel.r_addend = 0;
-		  bfd_put_NN (output_bfd, 0, got->contents + off);
-		  if (resolved_local && bfd_link_executable (info))
-		    bfd_put_NN (output_bfd, 1, got->contents + off);
-		  else if (resolved_local /* && !bfd_link_executable (info) */)
-		    {
-		      outrel.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_DTPMODNN);
-		      loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
-						 &outrel);
-		    }
-		  else /* if (resolved_dynly) */
+		  rela.r_offset = sec_addr (got) + got_off;
+		  rela.r_addend = 0;
+		  if (SYMBOL_REFERENCES_LOCAL (info, h))
 		    {
-		      outrel.r_info =
-			ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_DTPMODNN);
-		      loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
-						 &outrel);
-		    }
+		      /* Local sym, used in exec, set module id 1.  */
+		      if (bfd_link_executable (info))
+			bfd_put_NN (output_bfd, 1, got->contents + got_off);
+		      else
+			{
+			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_DTPMODNN);
+			  loongarch_elf_append_rela (output_bfd, relgot, &rela);
+			}
 
-		  outrel.r_offset += GOT_ENTRY_SIZE;
-		  bfd_put_NN (output_bfd, tls_block_off,
-			      got->contents + off + GOT_ENTRY_SIZE);
-		  if (resolved_local)
-		    /* DTPREL known.  */;
-		  else /* if (resolved_dynly) */
+		      bfd_put_NN (output_bfd, tls_block_off,
+				  got->contents + got_off + GOT_ENTRY_SIZE);
+		    }
+		  /* Dynamic resolved.  */
+		  else
 		    {
-		      outrel.r_info =
-			ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_DTPRELNN);
-		      loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
-						 &outrel);
+		      /* Dynamic relocate module id.  */
+		      rela.r_info = ELFNN_R_INFO (h->dynindx,
+						  R_LARCH_TLS_DTPMODNN);
+		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
+
+		      /* Dynamic relocate offset of block.  */
+		      rela.r_offset += GOT_ENTRY_SIZE;
+		      rela.r_info = ELFNN_R_INFO (h->dynindx,
+						  R_LARCH_TLS_DTPRELNN);
+		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
 		    }
 		}
-
 	      if (tls_type & GOT_TLS_IE)
 		{
-		  outrel.r_offset = sec_addr (got) + off + ie_off;
-		  bfd_put_NN (output_bfd, tls_block_off,
-			      got->contents + off + ie_off);
-		  if (resolved_local && bfd_link_executable (info))
-		    /* TPREL known.  */;
-		  else if (resolved_local /* && !bfd_link_executable (info) */)
+		  rela.r_offset = sec_addr (got) + got_off + ie_off;
+		  if (SYMBOL_REFERENCES_LOCAL (info, h))
 		    {
-		      outrel.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
-		      outrel.r_addend = tls_block_off;
-		      loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
-						 &outrel);
-		    }
-		  else /* if (resolved_dynly) */
-		    {
-		      /* Static linking has no .dynsym table.  */
-		      if (!htab->elf.dynamic_sections_created)
-			{
-			  outrel.r_info =
-			    ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
-			  outrel.r_addend = 0;
-			}
-		      else
+		      /* Local sym, used in exec, set module id 1.  */
+		      if (!bfd_link_executable (info))
 			{
-			  outrel.r_info =
-			    ELFNN_R_INFO (h->dynindx, R_LARCH_TLS_TPRELNN);
-			  outrel.r_addend = 0;
+			  rela.r_info = ELFNN_R_INFO (0, R_LARCH_TLS_TPRELNN);
+			  rela.r_addend = tls_block_off;
+			  loongarch_elf_append_rela (output_bfd, relgot, &rela);
 			}
-		      loongarch_elf_append_rela (output_bfd, htab->elf.srelgot,
-						 &outrel);
+
+		      bfd_put_NN (output_bfd, tls_block_off,
+				  got->contents + got_off + ie_off);
+		    }
+		  /* Dynamic resolved.  */
+		  else
+		    {
+		      /* Dynamic relocate offset of block.  */
+		      rela.r_info = ELFNN_R_INFO (h->dynindx,
+						  R_LARCH_TLS_TPRELNN);
+		      rela.r_addend = 0;
+		      loongarch_elf_append_rela (output_bfd, relgot, &rela);
 		    }
 		}
 	    }
+	  relocation = (got_off & (~(bfd_vma)1)) + sec_addr (got)
+			+ (is_ie ? ie_off : 0);
+
+	  if (r_type == R_LARCH_TLS_LD_PC_HI20
+	      || r_type == R_LARCH_TLS_GD_PC_HI20
+	      || r_type == R_LARCH_TLS_IE_PC_HI20)
+	    RELOCATE_CALC_PC32_HI20 (relocation, pc);
 
-	  relocation = off + (is_ie ? ie_off : 0);
+	  break;
+
+	case R_LARCH_TLS_IE_PC_LO12:
+	case R_LARCH_TLS_IE64_PC_LO20:
+	case R_LARCH_TLS_IE64_PC_HI12:
+	case R_LARCH_TLS_IE_LO12:
+	case R_LARCH_TLS_IE64_LO20:
+	case R_LARCH_TLS_IE64_HI12:
+	  unresolved_reloc = false;
+
+	  if (h)
+	    relocation = sec_addr (got) + (h->got.offset & (~(bfd_vma)3));
+	  else
+	    relocation = sec_addr (got)
+	      + (local_got_offsets[r_symndx] & (~(bfd_vma)3));
+
+	  tls_type = _bfd_loongarch_elf_tls_type (input_bfd, h, r_symndx);
+	  /* Use both TLS_GD and TLS_IE.  */
+	  if ((tls_type & GOT_TLS_GD) && (tls_type & GOT_TLS_IE))
+	    relocation += 2 * GOT_ENTRY_SIZE;
+
+	  if (r_type == R_LARCH_TLS_IE_PC_LO12)
+	    relocation &= (bfd_vma)0xfff;
+	  else if (r_type == R_LARCH_TLS_IE64_PC_LO20
+		   || r_type == R_LARCH_TLS_IE64_PC_HI12)
+	    RELOCATE_CALC_PC64_HI32 (relocation, pc);
+
+	  break;
+
+	case R_LARCH_RELAX:
 	  break;
 
 	default:
@@ -2759,7 +3137,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	  if (_bfd_elf_section_offset (output_bfd, info, input_section,
 				       rel->r_offset) == MINUS_ONE)
 	    /* WHY? May because it's invalid so skip checking.
-	       But why dynamic reloc a invalid section? */
+	       But why dynamic reloc a invalid section?  */
 	    break;
 
 	  if (input_section->output_section->flags & SEC_DEBUGGING)
@@ -2825,7 +3203,6 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	}
 
       fatal = true;
-      break;
     }
 
   return !fatal;
@@ -2889,27 +3266,15 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
       for (i = 0; i < PLT_ENTRY_INSNS; i++)
 	bfd_put_32 (output_bfd, plt_entry[i], loc + 4 * i);
 
-      /* Fill in the initial value of the .got.plt entry.  */
+      /* Fill in the initial value of the got.plt entry.  */
       loc = gotplt->contents + (got_address - sec_addr (gotplt));
       bfd_put_NN (output_bfd, sec_addr (plt), loc);
 
       rela.r_offset = got_address;
 
-      /* TRUE if this is a PLT reference to a local IFUNC.  */
-      if (PLT_LOCAL_IFUNC_P(info, h))
-	{
-	  rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
-	  rela.r_addend = (h->root.u.def.value
-			   + h->root.u.def.section->output_section->vma
-			   + h->root.u.def.section->output_offset);
-	}
-      else
-	{
-	  /* Fill in the entry in the .rela.plt section.  */
-	  rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT);
-	  rela.r_addend = 0;
-	}
-
+      /* Fill in the entry in the rela.plt section.  */
+      rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT);
+      rela.r_addend = 0;
       loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
       bed->s->swap_reloca_out (output_bfd, &rela, loc);
 
@@ -2930,15 +3295,14 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
   if (h->got.offset != MINUS_ONE
       /* TLS got entry have been handled in elf_relocate_section.  */
       && !(loongarch_elf_hash_entry (h)->tls_type & (GOT_TLS_GD | GOT_TLS_IE))
-      /* have allocated got entry but not allocated rela before.  */
+      /* Have allocated got entry but not allocated rela before.  */
       && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
     {
       asection *sgot, *srela;
       Elf_Internal_Rela rela;
-      bfd_vma off = h->got.offset & ~(bfd_vma) 1;
+      bfd_vma off = h->got.offset & ~(bfd_vma)1;
 
       /* This symbol has an entry in the GOT.  Set it up.  */
-
       sgot = htab->elf.sgot;
       srela = htab->elf.srelgot;
       BFD_ASSERT (sgot && srela);
@@ -2963,7 +3327,6 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
 		}
 	      else
 		{
-		  BFD_ASSERT ((h->got.offset & 1) == 0);
 		  BFD_ASSERT (h->dynindx != -1);
 		  rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN);
 		  rela.r_addend = 0;
@@ -2993,7 +3356,6 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
 	}
       else if (bfd_link_pic (info) && SYMBOL_REFERENCES_LOCAL (info, h))
 	{
-	  BFD_ASSERT (h->got.offset & 1 /* Has been filled in addr.  */);
 	  asection *sec = h->root.u.def.section;
 	  rela.r_info = ELFNN_R_INFO (0, R_LARCH_RELATIVE);
 	  rela.r_addend = (h->root.u.def.value + sec->output_section->vma
@@ -3001,7 +3363,6 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
 	}
       else
 	{
-	  BFD_ASSERT ((h->got.offset & 1) == 0);
 	  BFD_ASSERT (h->dynindx != -1);
 	  rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN);
 	  rela.r_addend = 0;
@@ -3010,24 +3371,6 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
       loongarch_elf_append_rela (output_bfd, srela, &rela);
     }
 
-  if (h->needs_copy)
-    {
-      Elf_Internal_Rela rela;
-      asection *s;
-
-      /* This symbols needs a copy reloc.  Set it up.  */
-      BFD_ASSERT (h->dynindx != -1);
-
-      rela.r_offset = sec_addr (h->root.u.def.section) + h->root.u.def.value;
-      rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_COPY);
-      rela.r_addend = 0;
-      if (h->root.u.def.section == htab->elf.sdynrelro)
-	s = htab->elf.sreldynrelro;
-      else
-	s = htab->elf.srelbss;
-      loongarch_elf_append_rela (output_bfd, s, &rela);
-    }
-
   /* Mark some specially defined symbols as absolute.  */
   if (h == htab->elf.hdynamic || h == htab->elf.hgot || h == htab->elf.hplt)
     sym->st_shndx = SHN_ABS;
@@ -3337,7 +3680,6 @@ loongarch_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 #define PRPSINFO_OFFSET_PR_PS_ARGS  0x38
 #define PRPSINFO_SIZEOF_PR_PS_ARGS  0x50
 
-
 static bool
 loongarch_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
 {
diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c
index 4424969e579..f059f1a0c05 100644
--- a/bfd/elfxx-loongarch.c
+++ b/bfd/elfxx-loongarch.c
@@ -35,27 +35,30 @@ typedef struct loongarch_reloc_howto_type_struct
   reloc_howto_type howto;
   bfd_reloc_code_real_type bfd_type;
   bool (*adjust_reloc_bits)(reloc_howto_type *, bfd_vma *);
-}loongarch_reloc_howto_type;
+  const char *larch_reloc_type_name;
+} loongarch_reloc_howto_type;
 
 #define LOONGARCH_DEFAULT_HOWTO(r_name)					    \
   { HOWTO (R_LARCH_##r_name, 0, 4, 32, false, 0, complain_overflow_signed,  \
 	bfd_elf_generic_reloc, "R_LARCH_" #r_name, false, 0, ALL_ONES,	    \
-	false), BFD_RELOC_LARCH_##r_name, NULL }
+	false), BFD_RELOC_LARCH_##r_name, NULL, NULL }
 
 #define LOONGARCH_HOWTO(type, right, size, bits, pcrel, left, ovf, func,  \
-	    name, inplace, src_mask, dst_mask, pcrel_off, btype, afunc)	  \
+	    name, inplace, src_mask, dst_mask, pcrel_off, btype, afunc,lname) \
   { HOWTO(type, right, size, bits, pcrel, left, ovf, func, name,	  \
-	  inplace, src_mask, dst_mask, pcrel_off), btype, afunc }
+	  inplace, src_mask, dst_mask, pcrel_off), btype, afunc, lname }
 
 #define LOONGARCH_EMPTY_HOWTO(C) \
-  { EMPTY_HOWTO(C), BFD_RELOC_NONE, NULL }
-
-bool loongarch_gen_adjust_reloc_bits (reloc_howto_type *howto, bfd_vma *val);
-bool loongarch_adjust_reloc_bits_l16_xx5_h5 (reloc_howto_type *howto,
-					     bfd_vma *fix_val);
-bool loongarch_adjust_reloc_bits_l16_h10 (reloc_howto_type *howto,
-					  bfd_vma *val);
+  { EMPTY_HOWTO (C), BFD_RELOC_NONE, NULL, NULL }
 
+static bool
+reloc_bits (reloc_howto_type *howto, bfd_vma *val);
+static bool
+reloc_bits_b16 (reloc_howto_type *howto, bfd_vma *fix_val);
+static bool
+reloc_bits_b21 (reloc_howto_type *howto, bfd_vma *fix_val);
+static bool
+reloc_bits_b26 (reloc_howto_type *howto, bfd_vma *val);
 
 /* This does not include any relocation information, but should be
    good enough for GDB or objdump to read the file.  */
@@ -76,7 +79,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 0,				  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_NONE,		  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   /* 32 bit relocation.  */
   LOONGARCH_HOWTO (R_LARCH_32,		  /* type (1).  */
@@ -93,7 +97,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_32,			  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   /* 64 bit relocation.  */
   LOONGARCH_HOWTO (R_LARCH_64,		  /* type (2).  */
@@ -110,7 +115,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_64,			  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_RELATIVE,	  /* type (3).  */
 	 0,				  /* rightshift */
@@ -126,7 +132,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_NONE,		  /* undefined?  */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_COPY,	  /* type (4).  */
 	 0,				  /* rightshift */
@@ -141,8 +148,9 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 0,				  /* src_mask */
 	 0,				  /* dst_mask */
 	 false,				  /* pcrel_offset */
-	 BFD_RELOC_NONE,			  /* undefined?  */
-	 NULL),				  /* adjust_reloc_bits */
+	 BFD_RELOC_NONE,		  /* undefined?  */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_JUMP_SLOT,	  /* type (5).  */
 	 0,				  /* rightshift */
@@ -157,8 +165,9 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 0,				  /* src_mask */
 	 0,				  /* dst_mask */
 	 false,				  /* pcrel_offset */
-	 BFD_RELOC_NONE,			  /* undefined?  */
-	 NULL),				  /* adjust_reloc_bits */
+	 BFD_RELOC_NONE,		  /* undefined?  */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   /* Dynamic TLS relocations.  */
   LOONGARCH_HOWTO (R_LARCH_TLS_DTPMOD32,  /* type (6).  */
@@ -175,7 +184,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_DTPMOD32,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_TLS_DTPMOD64,  /* type (7).  */
 	 0,				  /* rightshift */
@@ -191,7 +201,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_DTPMOD64,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_TLS_DTPREL32,  /* type (8). */
 	 0,				  /* rightshift */
@@ -207,7 +218,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_DTPREL32,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_TLS_DTPREL64,  /* type (9).  */
 	 0,				  /* rightshift */
@@ -223,7 +235,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_DTPREL64,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_TLS_TPREL32,	  /* type (10).  */
 	 0,				  /* rightshift */
@@ -239,7 +252,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_TPREL32,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_TLS_TPREL64,	  /* type (11).  */
 	 0,				  /* rightshift */
@@ -255,7 +269,8 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_LARCH_TLS_TPREL64,	  /* bfd_reloc_code_real_type */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_IRELATIVE,	  /* type (12).  */
 	 0,				  /* rightshift */
@@ -271,63 +286,67 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 ALL_ONES,			  /* dst_mask */
 	 false,				  /* pcrel_offset */
 	 BFD_RELOC_NONE,		  /* undefined?  */
-	 NULL),				  /* adjust_reloc_bits */
+	 NULL,				  /* adjust_reloc_bits */
+	 NULL),				  /* larch_reloc_type_name */
 
-  LOONGARCH_EMPTY_HOWTO(13),
-  LOONGARCH_EMPTY_HOWTO(14),
-  LOONGARCH_EMPTY_HOWTO(15),
-  LOONGARCH_EMPTY_HOWTO(16),
-  LOONGARCH_EMPTY_HOWTO(17),
-  LOONGARCH_EMPTY_HOWTO(18),
-  LOONGARCH_EMPTY_HOWTO(19),
+  LOONGARCH_EMPTY_HOWTO (13),
+  LOONGARCH_EMPTY_HOWTO (14),
+  LOONGARCH_EMPTY_HOWTO (15),
+  LOONGARCH_EMPTY_HOWTO (16),
+  LOONGARCH_EMPTY_HOWTO (17),
+  LOONGARCH_EMPTY_HOWTO (18),
+  LOONGARCH_EMPTY_HOWTO (19),
 
   LOONGARCH_HOWTO (R_LARCH_MARK_LA,		/* type (20).  */
-	 0,				   	/* rightshift.  */
-	 0,				   	/* size.  */
-	 0,				  	/* bitsize.  */
+	 0,					/* rightshift.  */
+	 0,					/* size.  */
+	 0,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
 	 "R_LARCH_MARK_LA",			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask.  */
 	 0,					/* dst_mask.  */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_MARK_LA,		/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_MARK_PCREL,		/* type (21).  */
-	 0,				   	/* rightshift.  */
-	 0,				   	/* size.  */
-	 0,				  	/* bitsize.  */
+	 0,					/* rightshift.  */
+	 0,					/* size.  */
+	 0,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
 	 "R_LARCH_MARK_PCREL",			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask.  */
 	 0,					/* dst_mask.  */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_MARK_PCREL,		/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_PUSH_PCREL,	/* type (22).  */
-	 2,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 32,				  	/* bitsize.  */
+	 2,					/* rightshift.  */
+	 4,					/* size.  */
+	 32,					/* bitsize.  */
 	 true /* FIXME: somewhat use this.  */,	/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SOP_PUSH_PCREL",	    	/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SOP_PUSH_PCREL",		/* name.  */
+	 false,					/* partial_inplace.  */
 	 0x03ffffff,				/* src_mask.  */
 	 0x03ffffff,				/* dst_mask.  */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_PUSH_PCREL,	/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
   /* type 23-37.  */
   LOONGARCH_DEFAULT_HOWTO (SOP_PUSH_ABSOLUTE),
@@ -347,343 +366,984 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
   LOONGARCH_DEFAULT_HOWTO (SOP_IF_ELSE),
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_10_5,	  /* type (38).  */
-	 0,				   	  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 5,				  	  /* bitsize.  */
+	 0,					  /* rightshift.  */
+	 4,					  /* size.  */
+	 5,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 10,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_10_5",	    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 10,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_10_5",		  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x7c00,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_10_5,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	  /* adjust_reloc_bits */
+	 reloc_bits,				  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_U_10_12,	  /* type (39).  */
-	 0,				   	  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 12,				  	  /* bitsize.  */
+	 0,					  /* rightshift.  */
+	 4,					  /* size.  */
+	 12,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 10,				   	  /* bitpos.  */
-	 complain_overflow_unsigned,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_U_10_12",	    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 10,					  /* bitpos.  */
+	 complain_overflow_unsigned,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_U_10_12",		  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x3ffc00,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_U_10_12,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	  /* adjust_reloc_bits */
+	 reloc_bits,				  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_10_12,	  /* type (40).  */
-	 0,				   	  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 12,				  	  /* bitsize.  */
+	 0,					  /* rightshift.  */
+	 4,					  /* size.  */
+	 12,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 10,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_10_12",	    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 10,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_10_12",		  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x3ffc00,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_10_12,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	  /* adjust_reloc_bits */
+	 reloc_bits,				  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_10_16,	  /* type (41).  */
-	 0,				   	  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 16,				  	  /* bitsize.  */
+	 0,					  /* rightshift.  */
+	 4,					  /* size.  */
+	 16,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 10,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_10_16",	    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 10,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_10_16",		  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x3fffc00,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_10_16,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	  /* adjust_reloc_bits */
+	 reloc_bits,				  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_10_16_S2, /* type (42).  */
 	 2,					  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 16,				  	  /* bitsize.  */
+	 4,					  /* size.  */
+	 16,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 10,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_10_16_S2",    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 10,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_10_16_S2",	  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x3fffc00,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	  /* adjust_reloc_bits */
+	 reloc_bits_b16,			  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_5_20,	  /* type (43).  */
-	 0,				   	  /* rightshift.  */
-	 4,				   	  /* size.  */
-	 20,				  	  /* bitsize.  */
+	 0,					  /* rightshift.  */
+	 4,					  /* size.  */
+	 20,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 5,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_5_20",    	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 5,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_5_20",		  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0,					  /* src_mask */
 	 0x1ffffe0,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_5_20,	  /* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),        /* adjust_reloc_bits */
+	 reloc_bits,				  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_0_5_10_16_S2,
 						  /* type (44).  */
 	 2,					  /* rightshift.  */
 	 4,					  /* size.  */
-	 21,				  	  /* bitsize.  */
+	 21,					  /* bitsize.  */
 	 false,					  /* pc_relative.  */
-	 0,				   	  /* bitpos.  */
-	 complain_overflow_signed,	    	  /* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		  /* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_0_5_10_16_S2",  	  /* name.  */
-	 false,			       		  /* partial_inplace.  */
+	 0,					  /* bitpos.  */
+	 complain_overflow_signed,		  /* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			  /* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_0_5_10_16_S2",	  /* name.  */
+	 false,					  /* partial_inplace.  */
 	 0xfc0003e0,				  /* src_mask */
 	 0xfc0003e0,				  /* dst_mask */
 	 false,					  /* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2,
 						  /* bfd_reloc_code_real_type */
-	 loongarch_adjust_reloc_bits_l16_xx5_h5), /* adjust_reloc_bits */
+	 reloc_bits_b21,			  /* adjust_reloc_bits */
+	 NULL),					  /* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_S_0_10_10_16_S2,	/* type (45).  */
-	 2,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 26,				  	/* bitsize.  */
-	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_0_10_10_16_S2", 	/* name.  */
-	 false,			       		/* partial_inplace.  */
-	 0xfc000000,				/* src_mask */
-	 0xfc000000,				/* dst_mask */
+	 2,					/* rightshift.  */
+	 4,					/* size.  */
+	 26,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_0_10_10_16_S2",	/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x03ffffff,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2,
 						/* bfd_reloc_code_real_type */
-	 loongarch_adjust_reloc_bits_l16_h10),	/* adjust_reloc_bits */
+	 reloc_bits_b26,			/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
   LOONGARCH_HOWTO (R_LARCH_SOP_POP_32_U,	/* type (46).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 32,				  	/* bitsize.  */
-	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_unsigned,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SOP_POP_32_S_U",    		/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 32,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_unsigned,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SOP_POP_32_S_U",		/* name.  */
+	 false,					/* partial_inplace.  */
 	 0xffffffff00000000,			/* src_mask */
 	 0x00000000ffffffff,			/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SOP_POP_32_U,		/* bfd_reloc_code_real_type */
-	 loongarch_gen_adjust_reloc_bits),	/* adjust_reloc_bits */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_ADD8,	      	/* type (47).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 8,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_ADD8,		/* type (47).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 8,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_ADD8",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ADD8",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_ADD8,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_ADD16,	      	/* type (48).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 16,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_ADD16,		/* type (48).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 16,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_ADD16",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ADD16",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_ADD16,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_ADD24,	      	/* type (49).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 24,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_ADD24,		/* type (49).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 24,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_ADD24",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ADD24",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_ADD24,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_ADD32,	      	/* type (50).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 32,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_ADD32,		/* type (50).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 32,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_ADD32",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ADD32",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_ADD32,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_ADD64,	      	/* type (51).  */
-	 0,				   	/* rightshift.  */
-	 8,				   	/* size.  */
-	 64,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_ADD64,		/* type (51).  */
+	 0,					/* rightshift.  */
+	 8,					/* size.  */
+	 64,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_ADD64",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ADD64",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_ADD64,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_SUB8,	      	/* type (52).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 8,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_SUB8,		/* type (52).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 8,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SUB8",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SUB8",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SUB8,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_SUB16,	      	/* type (53).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 16,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_SUB16,		/* type (53).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 16,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SUB16",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SUB16",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SUB16,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_SUB24,	      	/* type (54).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 24,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_SUB24,		/* type (54).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 24,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SUB24",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SUB24",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SUB24,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_SUB32,	      	/* type (55).  */
-	 0,				   	/* rightshift.  */
-	 4,				   	/* size.  */
-	 32,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_SUB32,		/* type (55).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 32,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SUB32",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SUB32",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SUB32,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_SUB64,	      	/* type (56).  */
-	 0,				   	/* rightshift.  */
-	 8,				   	/* size.  */
-	 64,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_SUB64,		/* type (56).  */
+	 0,					/* rightshift.  */
+	 8,					/* size.  */
+	 64,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_SUB64",    			/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_SUB64",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 ALL_ONES,				/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_LARCH_SUB64,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_GNU_VTINHERIT,      	/* type (57).  */
-	 0,				   	/* rightshift.  */
-	 0,				   	/* size.  */
-	 0,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_GNU_VTINHERIT,	/* type (57).  */
+	 0,					/* rightshift.  */
+	 0,					/* size.  */
+	 0,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
-	 bfd_elf_generic_reloc,	       		/* special_function.  */
-	 "R_LARCH_GNU_VTINHERIT",  		/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GNU_VTINHERIT",		/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 0,					/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_NONE,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_GNU_VTENTRY,      	/* type (58).  */
-	 0,				   	/* rightshift.  */
-	 0,				   	/* size.  */
-	 0,				  	/* bitsize.  */
+  LOONGARCH_HOWTO (R_LARCH_GNU_VTENTRY,		/* type (58).  */
+	 0,					/* rightshift.  */
+	 0,					/* size.  */
+	 0,					/* bitsize.  */
 	 false,					/* pc_relative.  */
-	 0,				   	/* bitpos.  */
-	 complain_overflow_signed,	    	/* complain_on_overflow.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
 	 NULL,					/* special_function.  */
-	 "R_LARCH_GNU_VTENTRY",  		/* name.  */
-	 false,			       		/* partial_inplace.  */
+	 "R_LARCH_GNU_VTENTRY",			/* name.  */
+	 false,					/* partial_inplace.  */
 	 0,					/* src_mask */
 	 0,					/* dst_mask */
 	 false,					/* pcrel_offset */
 	 BFD_RELOC_NONE,			/* bfd_reloc_code_real_type */
-	 NULL),					/* adjust_reloc_bits */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
+
+  LOONGARCH_EMPTY_HOWTO (59),
+  LOONGARCH_EMPTY_HOWTO (60),
+  LOONGARCH_EMPTY_HOWTO (61),
+  LOONGARCH_EMPTY_HOWTO (62),
+  LOONGARCH_EMPTY_HOWTO (63),
+
+  /* New reloc types.  */
+  LOONGARCH_HOWTO (R_LARCH_B16,			/* type (64).  */
+	 2,					/* rightshift.  */
+	 4,					/* size.  */
+	 16,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_B16",				/* name.  */
+	 false,					/* partial_inplace.  */
+	 0x3fffc00,				/* src_mask */
+	 0x3fffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_B16,			/* bfd_reloc_code_real_type */
+	 reloc_bits_b16,			/* adjust_reloc_bits */
+	 "b16"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_B21,			/* type (65).  */
+	 2,					/* rightshift.  */
+	 4,					/* size.  */
+	 21,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_B21",				/* name.  */
+	 false,					/* partial_inplace.  */
+	 0xfc0003e0,				/* src_mask */
+	 0xfc0003e0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_B21,			/* bfd_reloc_code_real_type */
+	 reloc_bits_b21,			/* adjust_reloc_bits */
+	 "b21"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_B26,			/* type (66).  */
+	 2,					/* rightshift.  */
+	 4,					/* size.  */
+	 26,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_B26",				/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x03ffffff,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_B26,			/* bfd_reloc_code_real_type */
+	 reloc_bits_b26,			/* adjust_reloc_bits */
+	 "b26"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_ABS_HI20,		/* type (67).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ABS_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_ABS_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "abs_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_ABS_LO12,		/* type (68).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_unsigned,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ABS_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_ABS_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "abs_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_ABS64_LO20,		/* type (69).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ABS64_LO20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_ABS64_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "abs64_lo20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_ABS64_HI12,		/* type (70).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ABS64_HI12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_ABS64_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "abs64_hi12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_PCALA_HI20,		/* type (71).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_PCALA_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_PCALA_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "pc_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_PCALA_LO12,		/* type (72).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_PCALA_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_PCALA_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "pc_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_PCALA64_LO20,	/* type (73).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_PCALA64_LO20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_PCALA64_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "pc64_lo20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_PCALA64_HI12,	/* type (74).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_PCALA64_HI12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_PCALA64_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "pc64_hi12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT_PC_HI20,		/* type (75).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT_PC_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT_PC_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got_pc_hi20"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT_PC_LO12,		/* type (76).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT_PC_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT_PC_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got_pc_lo12"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT64_PC_LO20,	/* type (77).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT64_PC_LO20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT64_PC_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got64_pc_lo20"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT64_PC_HI12,	/* type (78).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT64_PC_HI12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT64_PC_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got64_pc_hi12"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT_HI20,		/* type (79).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT_LO12,		/* type (80).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT64_LO20,		/* type (81).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT64_LO20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT64_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got64_lo20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_GOT64_HI12,		/* type (82).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_GOT64_HI12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_GOT64_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "got64_hi12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LE_HI20,		/* type (83).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LE_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LE_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "le_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LE_LO12,		/* type (84).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LE_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LE_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "le_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LE64_LO20,	/* type (85).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LE64_LO20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LE64_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "le64_lo20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LE64_HI12,	/* type (86).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LE64_HI12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LE64_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "le64_hi12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE_PC_HI20,	/* type (87).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE_PC_HI20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE_PC_HI20,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie_pc_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE_PC_LO12,	/* type (88).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_unsigned,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE_PC_LO12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE_PC_LO12,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie_pc_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE64_PC_LO20,	/* type (89).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE64_PC_LO20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE64_PC_LO20,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie64_pc_lo20"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE64_PC_HI12,	/* type (90).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE64_PC_HI12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE64_PC_HI12,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie64_pc_hi12"),			/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE_HI20,	/* type (91).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE_LO12,		/* type (92).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE_LO12",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE_LO12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie_lo12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE64_LO20,	/* type (93).  */
+	 32,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE64_LO20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE64_LO20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie64_lo20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_IE64_HI12,	/* type (94).  */
+	 52,					/* rightshift.  */
+	 4,					/* size.  */
+	 12,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 10,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_IE64_HI12",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x3ffc00,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_IE64_HI12,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ie64_hi12"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LD_PC_HI20,	/* type (95).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LD_PC_HI20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LD_PC_HI20,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ld_pc_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_LD_HI20,		/* type (96).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_LD_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_LD_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "ld_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_GD_PC_HI20,	/* type (97).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_GD_PC_HI20",		/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_GD_PC_HI20,	/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "gd_pc_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_TLS_GD_HI20,		/* type (98).  */
+	 12,					/* rightshift.  */
+	 4,					/* size.  */
+	 20,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 5,					/* bitpos.  */
+	 complain_overflow_signed,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_TLS_GD_HI20",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0x1ffffe0,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_TLS_GD_HI20,		/* bfd_reloc_code_real_type */
+	 reloc_bits,				/* adjust_reloc_bits */
+	 "gd_hi20"),				/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_RELAX,		/* type (99).  */
+	 0,					/* rightshift */
+	 1,					/* size */
+	 0,					/* bitsize */
+	 false,					/* pc_relative */
+	 0,					/* bitpos */
+	 complain_overflow_dont,		/* complain_on_overflow */
+	 bfd_elf_generic_reloc,			/* special_function */
+	 "R_LARCH_RELAX",			/* name */
+	 false,					/* partial_inplace */
+	 0,					/* src_mask */
+	 0,					/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_RELAX,			/* bfd_reloc_code_real_type */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
+
 };
 
 reloc_howto_type *
@@ -697,8 +1357,6 @@ loongarch_elf_rtype_to_howto (bfd *abfd, unsigned int r_type)
       if (loongarch_howto_table[r_type].howto.type == r_type)
 	return (reloc_howto_type *)&loongarch_howto_table[r_type];
 
-      BFD_ASSERT (loongarch_howto_table[r_type].howto.type == r_type);
-
       for (size_t i = 0; i < ARRAY_SIZE (loongarch_howto_table); i++)
 	if (loongarch_howto_table[i].howto.type == r_type)
 	  return (reloc_howto_type *)&loongarch_howto_table[i];
@@ -734,6 +1392,17 @@ loongarch_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 {
   BFD_ASSERT (ARRAY_SIZE (loongarch_howto_table) == R_LARCH_count);
 
+  /* Fast search for new reloc types.  */
+  if (BFD_RELOC_LARCH_B16 <= code && code < BFD_RELOC_LARCH_RELAX)
+    {
+      BFD_ASSERT (BFD_RELOC_LARCH_RELAX - BFD_RELOC_LARCH_B16
+		  == R_LARCH_RELAX - R_LARCH_B16);
+      loongarch_reloc_howto_type *ht = NULL;
+      ht = &loongarch_howto_table[code - BFD_RELOC_LARCH_B16 + R_LARCH_B16];
+      BFD_ASSERT (ht->bfd_type == code);
+      return (reloc_howto_type *)ht;
+    }
+
   for (size_t i = 0; i < ARRAY_SIZE (loongarch_howto_table); i++)
     if (loongarch_howto_table[i].bfd_type == code)
       return (reloc_howto_type *)&loongarch_howto_table[i];
@@ -745,56 +1414,89 @@ loongarch_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
   return NULL;
 }
 
+bfd_reloc_code_real_type
+loongarch_larch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+				   const char *l_r_name)
+{
+  for (size_t i = 0; i < ARRAY_SIZE (loongarch_howto_table); i++)
+    {
+      loongarch_reloc_howto_type *lht = &loongarch_howto_table[i];
+      if ((NULL != lht->larch_reloc_type_name)
+	  && (0 == strcmp (lht->larch_reloc_type_name, l_r_name)))
+	return lht->bfd_type;
+    }
+
+  (*_bfd_error_handler) (_("%pB: unsupported relocation type name %s"),
+			 abfd, l_r_name);
+  bfd_set_error (bfd_error_bad_value);
+  return BFD_RELOC_NONE;
+}
+
+
+/* Functions for reloc bits field.
+   1.  Signed extend *fix_val.
+   2.  Return false if overflow.  */
+
 #define LARCH_RELOC_BFD_VMA_BIT_MASK(bitsize) \
   (~((((bfd_vma)0x1) << (bitsize)) - 1))
 
 /* Adjust val to perform insn
- * BFD_RELOC_LARCH_SOP_POP_32_S_10_5
- * BFD_RELOC_LARCH_SOP_POP_32_S_10_12
- * BFD_RELOC_LARCH_SOP_POP_32_U_10_12
- * BFD_RELOC_LARCH_SOP_POP_32_S_10_16
- * BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2
- * BFD_RELOC_LARCH_SOP_POP_32_S_5_20
- * BFD_RELOC_LARCH_SOP_POP_32_U.
-*/
-
-bool loongarch_gen_adjust_reloc_bits (reloc_howto_type *howto, bfd_vma *fix_val)
+   BFD_RELOC_LARCH_SOP_POP_32_S_10_5
+   BFD_RELOC_LARCH_SOP_POP_32_S_10_12
+   BFD_RELOC_LARCH_SOP_POP_32_U_10_12
+   BFD_RELOC_LARCH_SOP_POP_32_S_10_16
+   BFD_RELOC_LARCH_SOP_POP_32_S_5_20
+   BFD_RELOC_LARCH_SOP_POP_32_U.  */
+static bool
+reloc_bits (reloc_howto_type *howto, bfd_vma *fix_val)
 {
-  bfd_vma val = *fix_val;
-  /* Check val low bits if rightshift != 0, before rightshift  */
-  if (howto->rightshift
-      && (((0x1UL << howto->rightshift) - 1) & val))
+  bfd_signed_vma val = ((bfd_signed_vma)(*fix_val)) >> howto->rightshift;
+
+  /* Perform insn bits field.  */
+  val = val & (((bfd_vma)0x1 << howto->bitsize) - 1);
+  val <<= howto->bitpos;
+
+  *fix_val = (bfd_vma)val;
+
+  return true;
+}
+
+/* Adjust val to perform insn
+   R_LARCH_SOP_POP_32_S_10_16_S2
+   R_LARCH_B16.  */
+static bool
+reloc_bits_b16 (reloc_howto_type *howto, bfd_vma *fix_val)
+{
+  if (howto->complain_on_overflow != complain_overflow_signed)
+    return false;
+
+  bfd_signed_vma val = *fix_val;
+
+  /* Judge whether 4 bytes align.  */
+  if (val & ((0x1UL << howto->rightshift) - 1))
     return false;
 
   int bitsize = howto->bitsize + howto->rightshift;
+  bfd_signed_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
 
-  /* Return false if overflow.  */
-  if (howto->complain_on_overflow == complain_overflow_signed)
+  /* If val < 0, sign bit is 1.  */
+  if (sig_bit)
     {
-      bfd_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
-      /* If val < 0.  */
-      if (sig_bit)
-	{
-	  if ((LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1) & val)
-	      != LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1))
-	    return false;
-	}
-      else
-	{
-	  if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1) & val)
-	    return false;
-	}
+      /* Signed bits is 1.  */
+      if ((LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1) & val)
+	  != LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1))
+	return false;
     }
-  else if (howto->complain_on_overflow == complain_overflow_unsigned)
+  else
     {
+      /* Signed bits is 0.  */
       if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize) & val)
 	return false;
     }
-  else
-    return false;
 
   /* Perform insn bits field.  */
-  val = (val & ((0x1U << bitsize) - 1)) >> howto->rightshift;
+  val >>= howto->rightshift;
+  val = val & (((bfd_vma)0x1 << howto->bitsize) - 1);
   val <<= howto->bitpos;
 
   *fix_val = val;
@@ -802,22 +1504,24 @@ bool loongarch_gen_adjust_reloc_bits (reloc_howto_type *howto, bfd_vma *fix_val)
   return true;
 }
 
-/* Reloc type R_LARCH_SOP_POP_32_S_0_5_10_16_S2.  */
-bool loongarch_adjust_reloc_bits_l16_xx5_h5 (reloc_howto_type *howto,
-					     bfd_vma *fix_val)
+/* Reloc type :
+   R_LARCH_SOP_POP_32_S_0_5_10_16_S2
+   R_LARCH_B21.  */
+static bool
+reloc_bits_b21 (reloc_howto_type *howto,
+		bfd_vma *fix_val)
 {
-  bfd_vma val = *fix_val;
-  /* Check val low bits if rightshift != 0, before rightshift  */
-  if (howto->rightshift
-      && (((0x1UL << howto->rightshift) - 1) & val))
+  if (howto->complain_on_overflow != complain_overflow_signed)
     return false;
 
-  /* Return false if overflow.  */
-  if (howto->complain_on_overflow != complain_overflow_signed)
+  bfd_signed_vma val = *fix_val;
+
+  if (val & ((0x1UL << howto->rightshift) - 1))
     return false;
 
   int bitsize = howto->bitsize + howto->rightshift;
-  bfd_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
+  bfd_signed_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
+
   /* If val < 0.  */
   if (sig_bit)
     {
@@ -827,14 +1531,15 @@ bool loongarch_adjust_reloc_bits_l16_xx5_h5 (reloc_howto_type *howto,
     }
   else
     {
-      if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1) & val)
+      if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize) & val)
 	return false;
     }
 
   /* Perform insn bits field.  */
-  val = (val & ((0x1U << bitsize) - 1)) >> howto->rightshift;
+  val >>= howto->rightshift;
+  val = val & (((bfd_vma)0x1 << howto->bitsize) - 1);
 
-  /* Perform insn bits field. 20:16>>16, 15:0<<10 */
+  /* Perform insn bits field.  15:0<<10, 20:16>>16.  */
   val = ((val & 0xffff) << 10) | ((val >> 16) & 0x1f);
 
   *fix_val = val;
@@ -842,22 +1547,25 @@ bool loongarch_adjust_reloc_bits_l16_xx5_h5 (reloc_howto_type *howto,
   return true;
 }
 
-/* Reloc type R_LARCH_SOP_POP_32_S_0_10_10_16_S2.  */
-bool loongarch_adjust_reloc_bits_l16_h10 (reloc_howto_type *howto,
-					  bfd_vma *fix_val)
+/* Reloc type:
+   R_LARCH_SOP_POP_32_S_0_10_10_16_S2
+   R_LARCH_B26.  */
+static bool
+reloc_bits_b26 (reloc_howto_type *howto,
+		bfd_vma *fix_val)
 {
-  bfd_vma val = *fix_val;
-  /* Check val low bits if rightshift != 0, before rightshift  */
-  if (howto->rightshift
-      && (((0x1UL << howto->rightshift) - 1) & val))
-    return false;
-
   /* Return false if overflow.  */
   if (howto->complain_on_overflow != complain_overflow_signed)
     return false;
 
+  bfd_signed_vma val = *fix_val;
+
+  if (val & ((0x1UL << howto->rightshift) - 1))
+    return false;
+
   int bitsize = howto->bitsize + howto->rightshift;
-  bfd_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
+  bfd_signed_vma sig_bit = (val >> (bitsize - 1)) & 0x1;
+
   /* If val < 0.  */
   if (sig_bit)
     {
@@ -867,14 +1575,15 @@ bool loongarch_adjust_reloc_bits_l16_h10 (reloc_howto_type *howto,
     }
   else
     {
-      if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize - 1) & val)
+      if (LARCH_RELOC_BFD_VMA_BIT_MASK (bitsize) & val)
 	return false;
     }
 
   /* Perform insn bits field.  */
-  val = (val & ((0x1U << bitsize) - 1)) >> howto->rightshift;
+  val >>= howto->rightshift;
+  val = val & (((bfd_vma)0x1 << howto->bitsize) - 1);
 
-  /* Perform insn bits field. 25:16>>16, 15:0<<10 */
+  /* Perform insn bits field.  25:16>>16, 15:0<<10.  */
   val = ((val & 0xffff) << 10) | ((val >> 16) & 0x3ff);
 
   *fix_val = val;
@@ -882,8 +1591,9 @@ bool loongarch_adjust_reloc_bits_l16_h10 (reloc_howto_type *howto,
   return true;
 }
 
-bool loongarch_adjust_reloc_bitsfield (reloc_howto_type *howto,
-				       bfd_vma *fix_val)
+bool
+loongarch_adjust_reloc_bitsfield (reloc_howto_type *howto,
+				  bfd_vma *fix_val)
 {
   BFD_ASSERT (((loongarch_reloc_howto_type *)howto)->adjust_reloc_bits);
   return ((loongarch_reloc_howto_type *)
diff --git a/bfd/elfxx-loongarch.h b/bfd/elfxx-loongarch.h
index 8ea63d03fa5..7b8a72130f1 100644
--- a/bfd/elfxx-loongarch.h
+++ b/bfd/elfxx-loongarch.h
@@ -30,6 +30,10 @@ loongarch_reloc_type_lookup (bfd *abfd, bfd_reloc_code_real_type code);
 extern reloc_howto_type *
 loongarch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name);
 
+extern bfd_reloc_code_real_type
+loongarch_larch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+				   const char *l_r_name);
+
 bool loongarch_adjust_reloc_bitsfield (reloc_howto_type *howto, bfd_vma *fix_val);
 
 /* TRUE if this is a PLT reference to a local IFUNC.  */
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 29e8187f95f..7eaa75ad11e 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3457,6 +3457,42 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_LARCH_SUB24",
   "BFD_RELOC_LARCH_SUB32",
   "BFD_RELOC_LARCH_SUB64",
+  "BFD_RELOC_LARCH_B16",
+  "BFD_RELOC_LARCH_B21",
+  "BFD_RELOC_LARCH_B26",
+  "BFD_RELOC_LARCH_ABS_HI20",
+  "BFD_RELOC_LARCH_ABS_LO12",
+  "BFD_RELOC_LARCH_ABS64_LO20",
+  "BFD_RELOC_LARCH_ABS64_HI12",
+  "BFD_RELOC_LARCH_PCALA_HI20",
+  "BFD_RELOC_LARCH_PCALA_LO12",
+  "BFD_RELOC_LARCH_PCALA64_LO20",
+  "BFD_RELOC_LARCH_PCALA64_HI12",
+  "BFD_RELOC_LARCH_GOT_PC_HI20",
+  "BFD_RELOC_LARCH_GOT_PC_LO12",
+  "BFD_RELOC_LARCH_GOT64_PC_LO20",
+  "BFD_RELOC_LARCH_GOT64_PC_HI12",
+  "BFD_RELOC_LARCH_GOT_HI20",
+  "BFD_RELOC_LARCH_GOT_LO12",
+  "BFD_RELOC_LARCH_GOT64_LO20",
+  "BFD_RELOC_LARCH_GOT64_HI12",
+  "BFD_RELOC_LARCH_TLS_LE_HI20",
+  "BFD_RELOC_LARCH_TLS_LE_LO12",
+  "BFD_RELOC_LARCH_TLS_LE64_LO20",
+  "BFD_RELOC_LARCH_TLS_LE64_HI12",
+  "BFD_RELOC_LARCH_TLS_IE_PC_HI20",
+  "BFD_RELOC_LARCH_TLS_IE_PC_LO12",
+  "BFD_RELOC_LARCH_TLS_IE64_PC_LO20",
+  "BFD_RELOC_LARCH_TLS_IE64_PC_HI12",
+  "BFD_RELOC_LARCH_TLS_IE_HI20",
+  "BFD_RELOC_LARCH_TLS_IE_LO12",
+  "BFD_RELOC_LARCH_TLS_IE64_LO20",
+  "BFD_RELOC_LARCH_TLS_IE64_HI12",
+  "BFD_RELOC_LARCH_TLS_LD_PC_HI20",
+  "BFD_RELOC_LARCH_TLS_LD_HI20",
+  "BFD_RELOC_LARCH_TLS_GD_PC_HI20",
+  "BFD_RELOC_LARCH_TLS_GD_HI20",
+  "BFD_RELOC_LARCH_RELAX",
  "@@overflow: BFD_RELOC_UNUSED@@",
 };
 #endif
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 36999fe9a40..59c2aaa0d31 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8228,6 +8228,85 @@ ENUMX
   BFD_RELOC_LARCH_SUB32
 ENUMX
   BFD_RELOC_LARCH_SUB64
+
+ENUMX
+  BFD_RELOC_LARCH_B16
+ENUMX
+  BFD_RELOC_LARCH_B21
+ENUMX
+  BFD_RELOC_LARCH_B26
+
+ENUMX
+  BFD_RELOC_LARCH_ABS_HI20
+ENUMX
+  BFD_RELOC_LARCH_ABS_LO12
+ENUMX
+  BFD_RELOC_LARCH_ABS64_LO20
+ENUMX
+  BFD_RELOC_LARCH_ABS64_HI12
+
+ENUMX
+  BFD_RELOC_LARCH_PCALA_HI20
+ENUMX
+  BFD_RELOC_LARCH_PCALA_LO12
+ENUMX
+  BFD_RELOC_LARCH_PCALA64_LO20
+ENUMX
+  BFD_RELOC_LARCH_PCALA64_HI12
+
+ENUMX
+  BFD_RELOC_LARCH_GOT_PC_HI20
+ENUMX
+  BFD_RELOC_LARCH_GOT_PC_LO12
+ENUMX
+  BFD_RELOC_LARCH_GOT64_PC_LO20
+ENUMX
+  BFD_RELOC_LARCH_GOT64_PC_HI12
+ENUMX
+  BFD_RELOC_LARCH_GOT_HI20
+ENUMX
+  BFD_RELOC_LARCH_GOT_LO12
+ENUMX
+  BFD_RELOC_LARCH_GOT64_LO20
+ENUMX
+  BFD_RELOC_LARCH_GOT64_HI12
+
+ENUMX
+  BFD_RELOC_LARCH_TLS_LE_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_LE_LO12
+ENUMX
+  BFD_RELOC_LARCH_TLS_LE64_LO20
+ENUMX
+  BFD_RELOC_LARCH_TLS_LE64_HI12
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE_PC_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE_PC_LO12
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE64_PC_LO20
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE64_PC_HI12
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE_LO12
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE64_LO20
+ENUMX
+  BFD_RELOC_LARCH_TLS_IE64_HI12
+ENUMX
+  BFD_RELOC_LARCH_TLS_LD_PC_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_LD_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_GD_PC_HI20
+ENUMX
+  BFD_RELOC_LARCH_TLS_GD_HI20
+
+ENUMX
+  BFD_RELOC_LARCH_RELAX
+
 ENUMDOC
   LARCH relocations.
 
diff --git a/include/elf/loongarch.h b/include/elf/loongarch.h
index b7aa4ff069c..2d2bb6a5161 100644
--- a/include/elf/loongarch.h
+++ b/include/elf/loongarch.h
@@ -90,6 +90,142 @@ RELOC_NUMBER (R_LARCH_SUB64, 56)
 RELOC_NUMBER (R_LARCH_GNU_VTINHERIT, 57)
 RELOC_NUMBER (R_LARCH_GNU_VTENTRY, 58)
 
+
+/* B16:
+   beq/bne/blt/bge/bltu/bgeu/jirl
+   %b16 (sym).  */
+RELOC_NUMBER (R_LARCH_B16, 64)
+/* B21:
+   beqz/bnez
+   %b16 (sym).  */
+RELOC_NUMBER (R_LARCH_B21, 65)
+/* B26:
+   b/bl
+   %b26 (sym) or %plt (sym).  */
+RELOC_NUMBER (R_LARCH_B26, 66)
+
+/* ABS: 32/64
+   lu12i.w
+   %abs_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_ABS_HI20, 67)
+/* ABS: 32/64
+   ori
+   %abs_lo12 (sym).  */
+RELOC_NUMBER (R_LARCH_ABS_LO12, 68)
+
+/* ABS: 64
+   lu32i.d
+   %abs64_lo20 (sym).  */
+RELOC_NUMBER (R_LARCH_ABS64_LO20, 69)
+/* ABS: 64
+   lu52i.d
+   %abs64_hi12 (sym).  */
+RELOC_NUMBER (R_LARCH_ABS64_HI12, 70)
+
+/* PCREL: 32/64
+   pcalau12i
+   %pc_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_PCALA_HI20, 71)
+/* PCREL: 32/64
+   addi.w/addi.d
+   %pc_lo12 (sym).  */
+RELOC_NUMBER (R_LARCH_PCALA_LO12, 72)
+/* PCREL: 64
+   lu32i.d
+   %pc64_lo20 (sym).  */
+RELOC_NUMBER (R_LARCH_PCALA64_LO20, 73)
+/* PCREL: 64
+   lu52i.d
+   %pc64_hi12 (sym).  */
+RELOC_NUMBER (R_LARCH_PCALA64_HI12, 74)
+
+/* GOT: 32/64
+   pcalau12i
+   %got_pc_hi20 (got).  */
+RELOC_NUMBER (R_LARCH_GOT_PC_HI20, 75)
+/* GOT: 32/64
+   ld.w/ld.d
+   %got_pc_lo12 (got).  */
+RELOC_NUMBER (R_LARCH_GOT_PC_LO12, 76)
+/* GOT: 32/64
+   lu32i.d
+   %got_pc_lo12 (got).  */
+RELOC_NUMBER (R_LARCH_GOT64_PC_LO20, 77)
+/* GOT64: PCREL
+   lu52i.d
+   %got64_pc_hi12 (got).  */
+RELOC_NUMBER (R_LARCH_GOT64_PC_HI12, 78)
+/* GOT32/64: ABS
+   lu12i.w
+   %got_hi20 (got).  */
+RELOC_NUMBER (R_LARCH_GOT_HI20, 79)
+/* GOT: 32/64: ABS
+   ori
+   %got_lo12 (got).  */
+RELOC_NUMBER (R_LARCH_GOT_LO12, 80)
+/* GOT64: ABS
+   lu32i.d
+   %got64_lo20 (got).  */
+RELOC_NUMBER (R_LARCH_GOT64_LO20, 81)
+/* GOT64: ABS
+   lu52i.d
+   %got64_hi12 (got).  */
+RELOC_NUMBER (R_LARCH_GOT64_HI12, 82)
+
+/* TLS-LE: 32/64
+   lu12i.w
+   %le_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LE_HI20, 83)
+/* TLS-LE: 32/64
+   ori
+   %le_lo12 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LE_LO12, 84)
+/* TLS-LE: 64
+   lu32i.d
+   %le64_lo20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LE64_LO20, 85)
+/* TLS-LE: 64
+   lu52i.d
+   %le64_hi12 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LE64_HI12, 86)
+
+/* TLS-IE: 32/64
+   pcalau12i
+   %ie_pc_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_IE_PC_HI20, 87)
+RELOC_NUMBER (R_LARCH_TLS_IE_PC_LO12, 88)
+RELOC_NUMBER (R_LARCH_TLS_IE64_PC_LO20, 89)
+RELOC_NUMBER (R_LARCH_TLS_IE64_PC_HI12, 90)
+
+/* TLS-IE: 32/64: ABS
+   lu12i.w
+   %ie_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_IE_HI20, 91)
+RELOC_NUMBER (R_LARCH_TLS_IE_LO12, 92)
+RELOC_NUMBER (R_LARCH_TLS_IE64_LO20, 93)
+RELOC_NUMBER (R_LARCH_TLS_IE64_HI12, 94)
+
+/* TLS-LD: 32/64
+   pcalau12i
+   %ld_pc_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LD_PC_HI20, 95)
+/* TLS-LD: 32/64: ABS
+   lu12i.w
+   %ld_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_LD_HI20, 96)
+
+/* TLS-GD: 32/64
+   pcalau12i
+   %gd_pc_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_GD_PC_HI20, 97)
+/* TLS-GD: 32/64: ABS
+   lu12i.w
+   %gd_hi20 (sym).  */
+RELOC_NUMBER (R_LARCH_TLS_GD_HI20, 98)
+
+/* RELAX.  */
+RELOC_NUMBER (R_LARCH_RELAX, 99)
+
 END_RELOC_NUMBERS (R_LARCH_count)
 
 /* Processor specific flags for the ELF header e_flags field.  */
-- 
2.31.1


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

* [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types.
  2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
  2022-07-21  1:37 ` [PATCH v2 1/6] bfd: Add supported for LoongArch new relocations liuzhensong
@ 2022-07-21  1:37 ` liuzhensong
  2022-07-21  2:30   ` Xi Ruoyao
  2022-07-21  1:37 ` [PATCH v2 3/6] LoongArch: gas: " liuzhensong
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

  opcodes: Replace old insns with news and generate new relocate types
  while macro insns expanding.

  opcodes/
    loongarch-opc.c
---
 opcodes/loongarch-opc.c | 412 +++++++++++++++++++++++-----------------
 1 file changed, 242 insertions(+), 170 deletions(-)

diff --git a/opcodes/loongarch-opc.c b/opcodes/loongarch-opc.c
index 62a2edb1ca9..be0de61c3aa 100644
--- a/opcodes/loongarch-opc.c
+++ b/opcodes/loongarch-opc.c
@@ -103,158 +103,230 @@ const char *const loongarch_x_normal_name[32] =
   "$xr24", "$xr25", "$xr26", "$xr27", "$xr28", "$xr29", "$xr30", "$xr31",
 };
 
+/* Can not use xx_pa for abs.  */
+
+/* For LoongArch32 abs.  */
+#define INSN_LA_ABS32		    \
+  "lu12i.w %1,%%abs_hi20(%2);"	    \
+  "ori %1,%1,%%abs_lo12(%2);",	    \
+  &LARCH_opts.ase_ilp32,	    \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_ABS64			\
+  "lu12i.w %1,%%abs_hi20(%2);"		\
+  "ori %1,%1,%%abs_lo12(%2);"		\
+  "lu32i.d %1,%%abs64_lo20(%2);"	\
+  "lu52i.d %1,%1,%%abs64_hi12(%2);",	\
+  &LARCH_opts.ase_lp64, 0
+
+#define INSN_LA_PCREL32		    \
+  "pcalau12i %1,%%pc_hi20(%2);"	    \
+  "addi.w %1,%1,%%pc_lo12(%2);",    \
+  &LARCH_opts.ase_ilp32,	    \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_PCREL64		    \
+  "pcalau12i %1,%%pc_hi20(%2);"	    \
+  "addi.d %1,%1,%%pc_lo12(%2);",    \
+  &LARCH_opts.ase_lp64, 0
+#define INSN_LA_PCREL64_LARGE	    \
+  "pcalau12i %1,%%pc_hi20(%3);"	    \
+  "addi.d %2,$r0,%%pc_lo12(%3);"    \
+  "lu32i.d %2,%%pc64_lo20(%3);"	    \
+  "lu52i.d %2,%2,%%pc64_hi12(%3);"  \
+  "add.d %1,%1,%2;",		    \
+  &LARCH_opts.ase_lp64, 0
+
+#define INSN_LA_GOT32		    \
+  "pcalau12i %1,%%got_pc_hi20(%2);" \
+  "ld.w %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_ilp32,	    \
+  &LARCH_opts.ase_lp64
+/* got32 abs.  */
+#define INSN_LA_GOT32_ABS	    \
+  "lu12i.w %1,%%got_hi20(%2);"      \
+  "ori %1,%1,%%got_lo12(%2);"	    \
+  "ld.w %1,%1,0;",  		    \
+  &LARCH_opts.ase_gabs,       	    \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_GOT64		    \
+  "pcalau12i %1,%%got_pc_hi20(%2);" \
+  "ld.d %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_lp64, 0
+/* got64 abs.  */
+#define INSN_LA_GOT64_LARGE_ABS	    \
+  "lu12i.w %1,%%got_hi20(%2);"      \
+  "ori %1,%1,%%got_lo12(%2);"	    \
+  "lu32i.d %1,%%got64_lo20(%2);"    \
+  "lu52i.d %1,%1,%%got64_hi12(%2);" \
+  "ld.d %1,%1,0",		    \
+  &LARCH_opts.ase_lp64,		    \
+  &LARCH_opts.ase_gpcr
+/* got64 pic.  */
+#define INSN_LA_GOT64_LARGE_PCREL     \
+  "pcalau12i %1,%%got_pc_hi20(%3);"   \
+  "addi.d %2,$r0,%%got_pc_lo12(%3);"  \
+  "lu32i.d %2,%%got64_pc_lo20(%3);"   \
+  "lu52i.d %2,%2,%%got64_pc_hi12(%3);"\
+  "ldx.d %1,%1,%2;",		      \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gabs
+
+/* For LoongArch32/64 cmode=normal.  */
+#define INSN_LA_TLS_LE		    \
+  "lu12i.w %1,%%le_hi20(%2);"	    \
+  "ori %1,%1,%%le_lo12(%2);",	    \
+  &LARCH_opts.ase_ilp32, 0
+
+/* For LoongArch64 cmode=large.  */
+#define INSN_LA_TLS_LE64_LARGE	    \
+  "lu12i.w %1,%%le_hi20(%2);"	    \
+  "ori %1,%1,%%le_lo12(%2);"	    \
+  "lu32i.d %1,%%le64_lo20(%2);"	    \
+  "lu52i.d %1,%1,%%le64_hi12(%2);", \
+  &LARCH_opts.ase_lp64, 0
+
+#define INSN_LA_TLS_IE32	    \
+  "pcalau12i %1,%%ie_pc_hi20(%2);"  \
+  "ld.w %1,%1,%%ie_pc_lo12(%2);",  \
+  &LARCH_opts.ase_ilp32,	    \
+  &LARCH_opts.ase_lp64
+/* For ie32 abs.  */
+#define INSN_LA_TLS_IE32_ABS  \
+  "lu12i.w %1,%%ie_hi20(%2);"	    \
+  "ori %1,%1,%%ie_lo12(%2);"	    \
+  "ld.w %1,%1,0",		    \
+  &LARCH_opts.ase_gabs,		    \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_TLS_IE64	    \
+  "pcalau12i %1,%%ie_pc_hi20(%2);"  \
+  "ld.d %1,%1,%%ie_pc_lo12(%2);",  \
+  &LARCH_opts.ase_lp64, 0
+/* For ie64 pic.  */
+#define INSN_LA_TLS_IE64_LARGE_PCREL  \
+  "pcalau12i %1,%%ie_pc_hi20(%3);"    \
+  "addi.d %2,$r0,%%ie_pc_lo12(%3);"  \
+  "lu32i.d %2,%%ie64_pc_lo20(%3);"   \
+  "lu52i.d %2,%2,%%ie64_pc_hi12(%3);"\
+  "ldx.d %1,%1,%2;",		      \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gabs
+/* For ie64 abs.  */
+#define INSN_LA_TLS_IE64_LARGE_ABS  \
+  "lu12i.w %1,%%ie_hi20(%2);"	    \
+  "ori %1,%1,%%ie_lo12(%2);"	    \
+  "lu32i.d %1,%%ie64_lo20(%2);"    \
+  "lu52i.d %1,%1,%%ie64_hi12(%2);" \
+  "ld.d %1,%1,0",		    \
+  &LARCH_opts.ase_lp64,		    \
+  &LARCH_opts.ase_gpcr
+
+/* For LoongArch32/64 cmode=normal.  */
+#define INSN_LA_TLS_LD32	      \
+  "pcalau12i %1,%%ld_pc_hi20(%2);"    \
+  "addi.w %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_ilp32,	      \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_TLS_LD32_ABS	      \
+  "lu12i.w %1,%%ld_hi20(%2);"	      \
+  "ori %1,%1,%%got_lo12(%2);",	      \
+  &LARCH_opts.ase_gabs,		      \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_TLS_LD64	      \
+  "pcalau12i %1,%%ld_pc_hi20(%2);"    \
+  "addi.d %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_lp64, 0
+#define INSN_LA_TLS_LD64_LARGE_PCREL  \
+  "pcalau12i %1,%%ld_pc_hi20(%3);"    \
+  "addi.d %2,$r0,%%got_pc_lo12(%3);"  \
+  "lu32i.d %2,%%got64_pc_lo20(%3);"   \
+  "lu52i.d %2,%2,%%got64_pc_hi12(%3);"\
+  "add.d %1,%1,%2;",		      \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gabs
+#define INSN_LA_TLS_LD64_LARGE_ABS    \
+  "lu12i.w %1,%%ld_hi20(%2);"	      \
+  "ori %1,%1,%%got_lo12(%2);"	      \
+  "lu32i.d %1,%%got64_lo20(%2);"      \
+  "lu52i.d %1,%1,%%got64_hi12(%2);",  \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gpcr
+
+#define INSN_LA_TLS_GD32	      \
+  "pcalau12i %1,%%gd_pc_hi20(%2);"    \
+  "addi.w %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_ilp32,	      \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_TLS_GD32_ABS	      \
+  "lu12i.w %1,%%gd_hi20(%2);"	      \
+  "ori %1,%1,%%got_lo12(%2);",	      \
+  &LARCH_opts.ase_gabs,		      \
+  &LARCH_opts.ase_lp64
+#define INSN_LA_TLS_GD64	      \
+  "pcalau12i %1,%%gd_pc_hi20(%2);"    \
+  "addi.d %1,%1,%%got_pc_lo12(%2);",  \
+  &LARCH_opts.ase_lp64, 0
+#define INSN_LA_TLS_GD64_LARGE_PCREL  \
+  "pcalau12i %1,%%gd_pc_hi20(%3);"    \
+  "addi.d %2,$r0,%%got_pc_lo12(%3);"  \
+  "lu32i.d %2,%%got64_pc_lo20(%3);"   \
+  "lu52i.d %2,%2,%%got64_pc_hi12(%3);"\
+  "add.d %1,%1,%2;",		      \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gabs
+#define INSN_LA_TLS_GD64_LARGE_ABS    \
+  "lu12i.w %1,%%gd_hi20(%2);"	      \
+  "ori %1,%1,%%got_lo12(%2);"	      \
+  "lu32i.d %1,%%got64_lo20(%2);"      \
+  "lu52i.d %1,%1,%%got64_hi12(%2);",  \
+  &LARCH_opts.ase_lp64,		      \
+  &LARCH_opts.ase_gpcr
+
+
 static struct loongarch_opcode loongarch_macro_opcodes[] =
 {
-  /* match,    mask,       name, format, macro, include, exclude, pinfo.  */
-  { 0, 0, "li.w", "r,sc", "%f", 0, 0, 0},
-  { 0, 0, "li.d", "r,sc", "%f", 0, 0, 0},
-  { 0, 0, "la", "r,la", "la.global %1,%2", 0, 0, 0 },
-
-  { 0, 0, "la.global", "r,la", "la.pcrel %1,%2",
-    &LARCH_opts.ase_gpcr, 0, 0 },
-  { 0, 0, "la.global", "r,r,la", "la.pcrel %1,%2,%3",
-    &LARCH_opts.ase_gpcr, 0, 0 },
-  { 0, 0, "la.global", "r,la", "la.abs %1,%2",
-    &LARCH_opts.ase_gabs, 0, 0 },
-  { 0, 0, "la.global", "r,r,la", "la.abs %1,%3",
-    &LARCH_opts.ase_gabs, 0, 0 },
-  { 0, 0, "la.global", "r,l", "la.got %1,%2", 0, 0, 0 },
-  { 0, 0, "la.global", "r,r,l", "la.got %1,%2,%3", 0, 0, 0 },
-
-  { 0, 0, "la.local", "r,la", "la.abs %1,%2",
-    &LARCH_opts.ase_labs, 0, 0 },
-  { 0, 0, "la.local", "r,r,la", "la.abs %1,%3",
-    &LARCH_opts.ase_labs, 0, 0 },
-  { 0, 0, "la.local", "r,la", "la.pcrel %1,%2", 0, 0, 0 },
-  { 0, 0, "la.local", "r,r,la", "la.pcrel %1,%2,%3", 0, 0, 0 },
-
-  { 0, 0, "la.abs", "r,la",
-    "lu12i.w %1,%%abs(%2)>>12;"
-    "ori %1,%1,%%abs(%2)&0xfff;",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-  { 0, 0, "la.abs", "r,la",
-    "lu12i.w %1,%%abs(%2)<<32>>44;"
-    "ori %1,%1,%%abs(%2)&0xfff;"
-    "lu32i.d %1,%%abs(%2)<<12>>44;"
-    "lu52i.d %1,%1,%%abs(%2)>>52;",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.pcrel", "r,la",
-    "pcaddu12i %1,%%pcrel(%2+0x800)<<32>>44;"
-    "addi.w %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-
-  { 0, 0, "la.pcrel", "r,la",
-    "pcaddu12i %1,%%pcrel(%2+0x800)>>12;"
-    "addi.d %1,%1,%%pcrel(%2+4)-(%%pcrel(%2+4+0x800)>>12<<12);",
-    &LARCH_opts.ase_lp64, 0, 0 },
-  { 0, 0, "la.pcrel", "r,r,la",
-    "pcaddu12i %1,(%%pcrel(%3)-(%%pcrel(%3+0x80000000)>>32<<32))<<32>>44;"
-    "ori %2,$r0,(%%pcrel(%3+4)-(%%pcrel(%3+4+0x80000000)>>32<<32))&0xfff;"
-    "lu32i.d %2,%%pcrel(%3+8+0x80000000)<<12>>44;"
-    "lu52i.d %2,%2,%%pcrel(%3+12+0x80000000)>>52;"
-    "add.d %1,%1,%2;",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.got", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))<<32>>44;"
-    "ld.w "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-
-  { 0, 0, "la.got", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%gprel(%2))>>12;"
-    "ld.d "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%gprel(%2))>>12<<12);",
-    &LARCH_opts.ase_lp64, 0, 0 },
-  { 0, 0, "la.got", "r,r,l",
-    "pcaddu12i "
-    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%gprel(%3)-((%%pcrel(_GLOBAL_OFFSET_"
-    "TABLE_+0x80000000)+%%gprel(%3))>>32<<32))<<32>>44;"
-    "ori "
-    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%gprel(%3)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x80000000)+%%gprel(%3))>>32<<32))&0xfff;"
-    "lu32i.d "
-    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%gprel(%3))<<12>>44;"
-    "lu52i.d "
-    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%gprel(%3))>>52;"
-    "ldx.d %1,%1,%2;",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.tls.le", "r,la",
-    "lu12i.w %1,%%tprel(%2)>>12;"
-    "ori %1,%1,%%tprel(%2)&0xfff",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-  /* { 0, 0, "la.tls.le", "r,la",
-  * "lu12i.w %1,%%tprel(%2)>>12;"
-  * "ori %1,%1,%%tprel(%2)&0xfff"
-  * , &LARCH_opts.addrwidth_is_64, 0, 0}, */
-  { 0, 0, "la.tls.le", "r,la",
-    "lu12i.w %1,%%tprel(%2)<<32>>44;"
-    "ori %1,%1,%%tprel(%2)&0xfff;"
-    "lu32i.d %1,%%tprel(%2)<<12>>44;"
-    "lu52i.d %1,%1,%%tprel(%2)>>52;",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.tls.ie", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))<<32>>44;"
-    "ld.w "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-
-  { 0, 0, "la.tls.ie", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgot(%2))>>12;"
-    "ld.d "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%tlsgot(%2))>>12<<12);",
-    &LARCH_opts.ase_lp64, 0, 0 },
-  { 0, 0, "la.tls.ie", "r,r,l",
-    "pcaddu12i "
-    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_OFFSET_"
-    "TABLE_+0x80000000)+%%tlsgot(%3))>>32<<32))<<32>>44;"
-    "ori "
-    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgot(%3)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x80000000)+%%tlsgot(%3))>>32<<32))&0xfff;"
-    "lu32i.d "
-    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgot(%3))<<12>>44;"
-    "lu52i.d "
-    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgot(%3))>>52;"
-    "ldx.d %1,%1,%2;",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.tls.ld", "r,l", "la.tls.gd %1,%2", 0, 0, 0 },
-  { 0, 0, "la.tls.ld", "r,r,l", "la.tls.gd %1,%2,%3",
-    &LARCH_opts.ase_lp64, 0, 0 },
-
-  { 0, 0, "la.tls.gd", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))<<32>>44;"
-    "addi.w "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);",
-    &LARCH_opts.ase_ilp32, &LARCH_opts.ase_lp64, 0 },
-
-  { 0, 0, "la.tls.gd", "r,l",
-    "pcaddu12i %1,(%%pcrel(_GLOBAL_OFFSET_TABLE_+0x800)+%%tlsgd(%2))>>12;"
-    "addi.d "
-    "%1,%1,%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%2)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x800)+%%tlsgd(%2))>>12<<12);",
-    &LARCH_opts.ase_lp64, 0, 0 },
-  { 0, 0, "la.tls.gd", "r,r,l",
-    "pcaddu12i "
-    "%1,(%%pcrel(_GLOBAL_OFFSET_TABLE_)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_OFFSET_"
-    "TABLE_+0x80000000)+%%tlsgd(%3))>>32<<32))<<32>>44;"
-    "ori "
-    "%2,$r0,(%%pcrel(_GLOBAL_OFFSET_TABLE_+4)+%%tlsgd(%3)-((%%pcrel(_GLOBAL_"
-    "OFFSET_TABLE_+4+0x80000000)+%%tlsgd(%3))>>32<<32))&0xfff;"
-    "lu32i.d "
-    "%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+8+0x80000000)+%%tlsgd(%3))<<12>>44;"
-    "lu52i.d "
-    "%2,%2,(%%pcrel(_GLOBAL_OFFSET_TABLE_+12+0x80000000)+%%tlsgd(%3))>>52;"
-    "add.d %1,%1,%2;",
-    &LARCH_opts.ase_lp64, 0, 0 },
+  /* match,    mask,	   name, format, macro, include, exclude, pinfo.  */
+  { 0, 0, "li.w", "r,sc", "%f",	0, 0, 0 },
+  { 0, 0, "li.d", "r,sc", "%f",	0, 0, 0 },
+
+  { 0, 0, "la",		"r,la",	  "la.global %1,%2",	0,			0, 0 },
+  { 0, 0, "la.global",	"r,la",	  "la.pcrel %1,%2",	&LARCH_opts.ase_gpcr,	0, 0 },
+  { 0, 0, "la.global",	"r,r,la", "la.pcrel %1,%2,%3",	&LARCH_opts.ase_gpcr,	0, 0 },
+  { 0, 0, "la.global",	"r,la",	  "la.abs %1,%2",	&LARCH_opts.ase_gabs,	0, 0 },
+  { 0, 0, "la.global",	"r,r,la", "la.abs %1,%3",	&LARCH_opts.ase_gabs,	0, 0 },
+  { 0, 0, "la.global",	"r,la",	  "la.got %1,%2",	0,			0, 0 },
+  { 0, 0, "la.global",	"r,r,la", "la.got %1,%2,%3",	&LARCH_opts.ase_lp64,	0, 0 },
+
+  { 0, 0, "la.local",	"r,la",	  "la.abs %1,%2",	&LARCH_opts.ase_labs,	0, 0 },
+  { 0, 0, "la.local",	"r,r,la", "la.abs %1,%3",	&LARCH_opts.ase_labs,	0, 0 },
+  { 0, 0, "la.local",	"r,la",	  "la.pcrel %1,%2",	0,			0, 0 },
+  { 0, 0, "la.local",	"r,r,la", "la.pcrel %1,%2,%3",	&LARCH_opts.ase_lp64,	0, 0 },
+
+  { 0, 0, "la.abs",	"r,la",	  INSN_LA_ABS32,		0 },
+  { 0, 0, "la.abs",	"r,la",	  INSN_LA_ABS64,		0 },
+  { 0, 0, "la.pcrel",	"r,la",	  INSN_LA_PCREL32,		0 },
+  { 0, 0, "la.pcrel",	"r,la",	  INSN_LA_PCREL64,		0 },
+  { 0, 0, "la.pcrel",	"r,r,la", INSN_LA_PCREL64_LARGE,	0 },
+  { 0, 0, "la.got",	"r,la",	  INSN_LA_GOT32,		0 },
+  { 0, 0, "la.got",	"r,la",	  INSN_LA_GOT32_ABS,		0 },
+  { 0, 0, "la.got",	"r,la",	  INSN_LA_GOT64,		0 },
+  { 0, 0, "la.got",	"r,la",	  INSN_LA_GOT64_LARGE_ABS,	0 },
+  { 0, 0, "la.got",	"r,r,la", INSN_LA_GOT64_LARGE_PCREL,	0 },
+  { 0, 0, "la.tls.le",	"r,l",	  INSN_LA_TLS_LE,		0 },
+  { 0, 0, "la.tls.le",	"r,l",	  INSN_LA_TLS_LE64_LARGE,	0 },
+  { 0, 0, "la.tls.ie",	"r,l",	  INSN_LA_TLS_IE32,		0 },
+  { 0, 0, "la.tls.ie",	"r,l",	  INSN_LA_TLS_IE32_ABS,		0 },
+  { 0, 0, "la.tls.ie",	"r,l",	  INSN_LA_TLS_IE64,		0 },
+  { 0, 0, "la.tls.ie",	"r,l",	  INSN_LA_TLS_IE64_LARGE_ABS,	0 },
+  { 0, 0, "la.tls.ie",	"r,r,l",  INSN_LA_TLS_IE64_LARGE_PCREL,	0 },
+  { 0, 0, "la.tls.ld",	"r,l",	  INSN_LA_TLS_LD32,		0 },
+  { 0, 0, "la.tls.ld",	"r,l",	  INSN_LA_TLS_LD32_ABS,		0 },
+  { 0, 0, "la.tls.ld",	"r,l",	  INSN_LA_TLS_LD64,		0 },
+  { 0, 0, "la.tls.ld",	"r,l",	  INSN_LA_TLS_LD64_LARGE_ABS,	0 },
+  { 0, 0, "la.tls.ld",	"r,r,l",  INSN_LA_TLS_LD64_LARGE_PCREL,	0 },
+  { 0, 0, "la.tls.gd",	"r,l",	  INSN_LA_TLS_GD32,		0 },
+  { 0, 0, "la.tls.gd",	"r,l",	  INSN_LA_TLS_GD32_ABS,		0 },
+  { 0, 0, "la.tls.gd",	"r,l",	  INSN_LA_TLS_GD64,		0 },
+  { 0, 0, "la.tls.gd",	"r,l",	  INSN_LA_TLS_GD64_LARGE_ABS,	0 },
+  { 0, 0, "la.tls.gd",	"r,r,l",  INSN_LA_TLS_GD64_LARGE_PCREL,	0 },
 
   { 0 } /* Terminate the list.  */
 };
@@ -728,9 +800,9 @@ static struct loongarch_opcode loongarch_double_float_load_store_opcodes[] =
 
 static struct loongarch_opcode loongarch_float_jmp_opcodes[] =
 {
-  { 0x0,	0x0,		"bceqz",	"c,la",				"bceqz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bceqz",	"c,la",				"bceqz %1,%%b21(%2)",		0, 0, 0 },
   { 0x48000000, 0xfc000300,	"bceqz",	"c5:3,sb0:5|10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bcnez",	"c,la",				"bcnez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bcnez",	"c,la",				"bcnez %1,%%b21(%2)",		0, 0, 0 },
   { 0x48000100, 0xfc000300,	"bcnez",	"c5:3,sb0:5|10:16<<2",		0,				0, 0, 0 },
   { 0 } /* Terminate the list.  */
 };
@@ -738,43 +810,43 @@ static struct loongarch_opcode loongarch_float_jmp_opcodes[] =
 static struct loongarch_opcode loongarch_jmp_opcodes[] =
 {
   /* match,	mask,		name,		format,				macro,			include, exclude, pinfo.  */
-  { 0x0,	0x0,		"bltz",		"r,la",				"bltz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bltz",		"r,la",				"bltz %1,%%b16(%2)",		0, 0, 0 },
   { 0x60000000, 0xfc00001f,	"bltz",		"r5:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bgtz",		"r,la",				"bgtz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bgtz",		"r,la",				"bgtz %1,%%b16(%2)",		0, 0, 0 },
   { 0x60000000, 0xfc0003e0,	"bgtz",		"r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bgez",		"r,la",				"bgez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bgez",		"r,la",				"bgez %1,%%b16(%2)",		0, 0, 0 },
   { 0x64000000, 0xfc00001f,	"bgez",		"r5:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"blez",		"r,la",				"blez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"blez",		"r,la",				"blez %1,%%b16(%2)",		0, 0, 0 },
   { 0x64000000, 0xfc0003e0,	"blez",		"r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"beqz",		"r,la",				"beqz %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"beqz",		"r,la",				"beqz %1,%%b21(%2)",		0, 0, 0 },
   { 0x40000000, 0xfc000000,	"beqz",		"r5:5,sb0:5|10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bnez",		"r,la",				"bnez %1,%%pcrel(%2)",		0, 0, 0 },
+  { 0x0,	0x0,		"bnez",		"r,la",				"bnez %1,%%b21(%2)",		0, 0, 0 },
   { 0x44000000, 0xfc000000,	"bnez",		"r5:5,sb0:5|10:16<<2",		0,				0, 0, 0 },
   { 0x0,	0x0,		"jr",		"r",				"jirl $r0,%1,0",		0, 0, 0 },
   { 0x50000000, 0xfc000000,	"b",		"sb0:10|10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"b",		"la",				"b %%pcrel(%1)",		0, 0, 0 },
+  { 0x0,	0x0,		"b",		"la",				"b %%b26(%1)",			0, 0, 0 },
   { 0x4c000000, 0xfc000000,	"jirl",		"r0:5,r5:5,s10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bl",		"la",				"bl %%pcrel(%1)",		0, 0, 0 },
+  { 0x0,	0x0,		"bl",		"la",				"bl %%b26(%1)",			0, 0, 0 },
   { 0x54000000, 0xfc000000,	"bl",		"sb0:10|10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"beq",		"r,r,la",			"beq %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"beq",		"r,r,la",			"beq %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x58000000, 0xfc000000,	"beq",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bne",		"r,r,la",			"bne %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bne",		"r,r,la",			"bne %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x5c000000, 0xfc000000,	"bne",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"blt",		"r,r,la",			"blt %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"blt",		"r,r,la",			"blt %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x60000000, 0xfc000000,	"blt",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bgt",		"r,r,la",			"bgt %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bgt",		"r,r,la",			"bgt %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x60000000, 0xfc000000,	"bgt",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bge",		"r,r,la",			"bge %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bge",		"r,r,la",			"bge %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x64000000, 0xfc000000,	"bge",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"ble",		"r,r,la",			"ble %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"ble",		"r,r,la",			"ble %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x64000000, 0xfc000000,	"ble",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bltu",		"r,r,la",			"bltu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bltu",		"r,r,la",			"bltu %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x68000000, 0xfc000000,	"bltu",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bgtu",		"r,r,la",			"bgtu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bgtu",		"r,r,la",			"bgtu %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x68000000, 0xfc000000,	"bgtu",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bgeu",		"r,r,la",			"bgeu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bgeu",		"r,r,la",			"bgeu %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x6c000000, 0xfc000000,	"bgeu",		"r5:5,r0:5,sb10:16<<2",		0,				0, 0, 0 },
-  { 0x0,	0x0,		"bleu",		"r,r,la",			"bleu %1,%2,%%pcrel(%3)",	0, 0, 0 },
+  { 0x0,	0x0,		"bleu",		"r,r,la",			"bleu %1,%2,%%b16(%3)",		0, 0, 0 },
   { 0x6c000000, 0xfc000000,	"bleu",		"r0:5,r5:5,sb10:16<<2",		0,				0, 0, 0 },
   { 0 } /* Terminate the list.  */
 };
-- 
2.31.1


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

* [PATCH v2 3/6] LoongArch: gas: Add new reloc types.
  2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
  2022-07-21  1:37 ` [PATCH v2 1/6] bfd: Add supported for LoongArch new relocations liuzhensong
  2022-07-21  1:37 ` [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types liuzhensong
@ 2022-07-21  1:37 ` liuzhensong
  2022-07-21  1:37 ` [PATCH v2 4/6] LoongArch: Move ifunc info to rela.dyn from rela.plt liuzhensong
  2022-07-21  1:37 ` [PATCH v2 5/6] bfd: Delete R_LARCH_NONE from dyn info of LoongArch liuzhensong
  4 siblings, 0 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

  Generate new relocate types while use new macro insns.

  gas/config/
    loongarch-lex.h
    loongarch-parse.y
    tc-loongarch.c
    tc-loongarch.h
---
 gas/config/loongarch-lex.h   |   3 +
 gas/config/loongarch-parse.y |  72 +++++-------------
 gas/config/tc-loongarch.c    | 140 ++++++++++++++++++++++++-----------
 gas/config/tc-loongarch.h    |   7 +-
 4 files changed, 120 insertions(+), 102 deletions(-)

diff --git a/gas/config/loongarch-lex.h b/gas/config/loongarch-lex.h
index 59212442242..35d22dbdbc9 100644
--- a/gas/config/loongarch-lex.h
+++ b/gas/config/loongarch-lex.h
@@ -32,3 +32,6 @@ loongarch_parse_expr (const char *expr,
 		      size_t max_reloc_num,
 		      size_t *reloc_num,
 		      offsetT *imm);
+bfd_reloc_code_real_type
+loongarch_larch_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
+				   const char *l_r_name);
diff --git a/gas/config/loongarch-parse.y b/gas/config/loongarch-parse.y
index 710f854c74f..902d7204e8e 100644
--- a/gas/config/loongarch-parse.y
+++ b/gas/config/loongarch-parse.y
@@ -83,11 +83,11 @@ static const char *
 my_getExpression (expressionS *ep, const char *str)
 {
   char *save_in, *ret;
+
   if (*str == ':')
     {
       unsigned long j;
       char *str_1 = (char *) str;
-      str_1++;
       j = strtol (str_1, &str_1, 10);
       get_internal_label (ep, j, *str_1 == 'f');
       return NULL;
@@ -104,65 +104,31 @@ static void
 reloc (const char *op_c_str, const char *id_c_str, offsetT addend)
 {
   expressionS id_sym_expr;
+  bfd_reloc_code_real_type btype;
 
   if (end <= top)
     as_fatal (_("expr too huge"));
 
-  if (id_c_str)
-    {
-      my_getExpression (&id_sym_expr, id_c_str);
-      id_sym_expr.X_add_number += addend;
-    }
+  /* For compatible old asm code.  */
+  if (0 == strcmp (op_c_str, "plt"))
+    btype = BFD_RELOC_LARCH_B26;
   else
-    {
-      id_sym_expr.X_op = O_constant;
-      id_sym_expr.X_add_number = addend;
-    }
+    btype = loongarch_larch_reloc_name_lookup (NULL, op_c_str);
 
-  if (strcmp (op_c_str, "abs") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE;
-      top++;
-    }
-  else if (strcmp (op_c_str, "pcrel") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_PCREL;
-      top++;
-    }
-  else if (strcmp (op_c_str, "gprel") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_GPREL;
-      top++;
-    }
-  else if (strcmp (op_c_str, "tprel") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL;
-      top++;
-    }
-  else if (strcmp (op_c_str, "tlsgot") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT;
-      top++;
-    }
-  else if (strcmp (op_c_str, "tlsgd") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GD;
-      top++;
-    }
-  else if (strcmp (op_c_str, "plt") == 0)
-    {
-      top->value = id_sym_expr;
-      top->type = BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL;
-      top++;
-    }
+  if (id_c_str)
+  {
+    my_getExpression (&id_sym_expr, id_c_str);
+    id_sym_expr.X_add_number += addend;
+  }
   else
-    as_fatal (_("unknown reloc hint: %s"), op_c_str);
+  {
+    id_sym_expr.X_op = O_constant;
+    id_sym_expr.X_add_number = addend;
+  }
+
+  top->value = id_sym_expr;
+  top->type = btype;
+  top++;
 }
 
 static void
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index 08203d291bd..fbbaca55085 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -362,18 +362,6 @@ loongarch_mach (void)
 
 static const expressionS const_0 = { .X_op = O_constant, .X_add_number = 0 };
 
-static const char *
-my_getExpression (expressionS *ep, const char *str)
-{
-  char *save_in, *ret;
-  save_in = input_line_pointer;
-  input_line_pointer = (char *) str;
-  expression (ep);
-  ret = input_line_pointer;
-  input_line_pointer = save_in;
-  return ret;
-}
-
 static void
 s_loongarch_align (int arg)
 {
@@ -480,11 +468,6 @@ get_internal_label (expressionS *label_expr, unsigned long label,
   label_expr->X_add_number = 0;
 }
 
-extern int loongarch_parse_expr (const char *expr,
-				 struct reloc_info *reloc_stack_top,
-				 size_t max_reloc_num, size_t *reloc_num,
-				 offsetT *imm_if_no_reloc);
-
 static int
 is_internal_label (const char *c_str)
 {
@@ -652,6 +635,15 @@ loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2,
 	    as_fatal (
 		      _("not support reloc bit-field\nfmt: %c%c %s\nargs: %s"),
 		      esc_ch1, esc_ch2, bit_field, arg);
+	  if (ip->reloc_info[0].type >= BFD_RELOC_LARCH_B16
+	      && ip->reloc_info[0].type < BFD_RELOC_LARCH_RELAX)
+	    {
+	      /* As we compact stack-relocs, it is no need for pop operation.
+		 But break out until here in order to check the imm field.
+		 May be reloc_num > 1 if implement relax?  */
+	      ip->reloc_num += reloc_num;
+	      break;
+	    }
 	  reloc_num++;
 	  ip->reloc_num += reloc_num;
 	  ip->reloc_info[ip->reloc_num - 1].type = reloc_type;
@@ -767,7 +759,12 @@ get_loongarch_opcode (struct loongarch_cl_insn *insn)
 	{
 	  ase->name_hash_entry = str_htab_create ();
 	  for (it = ase->opcodes; it->name; it++)
-	    str_hash_insert (ase->name_hash_entry, it->name, (void *) it, 0);
+	    {
+	      if ((!it->include || (it->include && *it->include))
+		  && (!it->exclude || (it->exclude && !(*it->exclude))))
+		str_hash_insert (ase->name_hash_entry, it->name,
+				 (void *) it, 0);
+	    }
 	}
 
       if ((it = str_hash_find (ase->name_hash_entry, insn->name)) == NULL)
@@ -800,10 +797,11 @@ static int
 check_this_insn_before_appending (struct loongarch_cl_insn *ip)
 {
   int ret = 0;
-  if (strcmp (ip->name, "la.abs") == 0)
+
+  if (strncmp (ip->name, "la.abs", 6) == 0)
     {
       ip->reloc_info[ip->reloc_num].type = BFD_RELOC_LARCH_MARK_LA;
-      my_getExpression (&ip->reloc_info[ip->reloc_num].value, ip->arg_strs[1]);
+      ip->reloc_info[ip->reloc_num].value = const_0;
       ip->reloc_num++;
     }
   else if (ip->insn->mask == 0xffff8000
@@ -980,6 +978,7 @@ assember_macro_helper (const char *const args[], void *context_ptr)
       ret = loongarch_expand_macro (insns_buf, arg_strs, NULL, NULL,
 				    sizeof (args_buf));
     }
+
   return ret;
 }
 
@@ -1096,6 +1095,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
   static int64_t stack_top;
   static int last_reloc_is_sop_push_pcrel_1 = 0;
   int last_reloc_is_sop_push_pcrel = last_reloc_is_sop_push_pcrel_1;
+  segT sub_segment;
   last_reloc_is_sop_push_pcrel_1 = 0;
 
   char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
@@ -1104,26 +1104,40 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL:
     case BFD_RELOC_LARCH_SOP_PUSH_TLS_GD:
     case BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT:
-    case BFD_RELOC_LARCH_SOP_PUSH_PCREL:
-    case BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL:
+    case BFD_RELOC_LARCH_TLS_LE_HI20:
+    case BFD_RELOC_LARCH_TLS_LE_LO12:
+    case BFD_RELOC_LARCH_TLS_LE64_LO20:
+    case BFD_RELOC_LARCH_TLS_LE64_HI12:
+    case BFD_RELOC_LARCH_TLS_IE_PC_HI20:
+    case BFD_RELOC_LARCH_TLS_IE_PC_LO12:
+    case BFD_RELOC_LARCH_TLS_IE64_PC_LO20:
+    case BFD_RELOC_LARCH_TLS_IE64_PC_HI12:
+    case BFD_RELOC_LARCH_TLS_IE_HI20:
+    case BFD_RELOC_LARCH_TLS_IE_LO12:
+    case BFD_RELOC_LARCH_TLS_IE64_LO20:
+    case BFD_RELOC_LARCH_TLS_IE64_HI12:
+    case BFD_RELOC_LARCH_TLS_LD_PC_HI20:
+    case BFD_RELOC_LARCH_TLS_LD_HI20:
+    case BFD_RELOC_LARCH_TLS_GD_PC_HI20:
+    case BFD_RELOC_LARCH_TLS_GD_HI20:
+      /* Add tls lo (got_lo reloc type).  */
       if (fixP->fx_addsy == NULL)
 	as_bad_where (fixP->fx_file, fixP->fx_line,
 		      _("Relocation against a constant"));
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      break;
 
-      if (fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL
-	  || fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_TLS_GD
-	  || fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT)
-	S_SET_THREAD_LOCAL (fixP->fx_addsy);
+    case BFD_RELOC_LARCH_SOP_PUSH_PCREL:
+      if (fixP->fx_addsy == NULL)
+	as_bad_where (fixP->fx_file, fixP->fx_line,
+		      _("Relocation against a constant"));
 
-      if (fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_PCREL)
-	{
-	  last_reloc_is_sop_push_pcrel_1 = 1;
-	  if (S_GET_SEGMENT (fixP->fx_addsy) == seg)
-	    stack_top = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
-			 - (fixP->fx_where + fixP->fx_frag->fr_address));
-	  else
-	    stack_top = 0;
-	}
+      last_reloc_is_sop_push_pcrel_1 = 1;
+      if (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+	stack_top = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+		     - (fixP->fx_where + fixP->fx_frag->fr_address));
+      else
+	stack_top = 0;
       break;
 
     case BFD_RELOC_LARCH_SOP_POP_32_S_10_5:
@@ -1143,11 +1157,24 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 
     case BFD_RELOC_64:
     case BFD_RELOC_32:
+    case BFD_RELOC_24:
+    case BFD_RELOC_16:
+    case BFD_RELOC_8:
+
+      if (fixP->fx_r_type == BFD_RELOC_32
+	  && fixP->fx_addsy && fixP->fx_subsy
+	  && (sub_segment = S_GET_SEGMENT (fixP->fx_subsy))
+	  && strcmp (sub_segment->name, ".eh_frame") == 0
+	  && S_GET_VALUE (fixP->fx_subsy)
+	  == fixP->fx_frag->fr_address + fixP->fx_where)
+	{
+	  fixP->fx_r_type = BFD_RELOC_LARCH_32_PCREL;
+	  fixP->fx_subsy = NULL;
+	  break;
+	}
+
       if (fixP->fx_subsy)
 	{
-	case BFD_RELOC_24:
-	case BFD_RELOC_16:
-	case BFD_RELOC_8:
 	  fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP));
 	  fixP->fx_next->fx_addsy = fixP->fx_subsy;
 	  fixP->fx_next->fx_subsy = NULL;
@@ -1190,6 +1217,25 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 	}
       break;
 
+    case BFD_RELOC_LARCH_B16:
+    case BFD_RELOC_LARCH_B21:
+    case BFD_RELOC_LARCH_B26:
+      if (fixP->fx_addsy == NULL)
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_ ("Relocation against a constant."));
+	}
+      if (S_GET_SEGMENT (fixP->fx_addsy) == seg
+	  && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+	{
+	  int64_t sym_addend = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset;
+	  int64_t pc = fixP->fx_where + fixP->fx_frag->fr_address;
+	  fix_reloc_insn (fixP, sym_addend - pc, buf);
+	  fixP->fx_done = 1;
+	}
+
+      break;
+
     default:
       break;
     }
@@ -1210,6 +1256,18 @@ md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
   return 0;
 }
 
+int
+loongarch_fix_adjustable (fixS *fix)
+{
+  /* Prevent all adjustments to global symbols.  */
+  if (S_IS_EXTERNAL (fix->fx_addsy)
+      || S_IS_WEAK (fix->fx_addsy)
+      || S_FORCE_RELOC (fix->fx_addsy, true))
+    return 0;
+
+  return 1;
+}
+
 /* Translate internal representation of relocation info to BFD target
    format.  */
 arelent *
@@ -1249,12 +1307,6 @@ loongarch_cfi_frame_initial_instructions (void)
   cfi_add_CFA_def_cfa_register (3 /* $sp */);
 }
 
-int
-loongarch_dwarf2_addr_size (void)
-{
-  return LARCH_opts.ase_lp64 ? 8 : 4;
-}
-
 void
 tc_loongarch_parse_to_dw2regnum (expressionS *exp)
 {
diff --git a/gas/config/tc-loongarch.h b/gas/config/tc-loongarch.h
index 2664da59f51..f05926d7d67 100644
--- a/gas/config/tc-loongarch.h
+++ b/gas/config/tc-loongarch.h
@@ -49,7 +49,8 @@ extern int loongarch_relax_frag (asection *, struct frag *, long);
 
 /* This is called to see whether a reloc against a defined symbol
    should be converted into a reloc against a section.  */
-#define tc_fix_adjustable(fixp) 0
+extern int loongarch_fix_adjustable (struct fix *fix);
+#define tc_fix_adjustable(fixp) loongarch_fix_adjustable(fixp)
 
 /* Values passed to md_apply_fix don't include symbol values.  */
 #define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
@@ -59,10 +60,6 @@ extern int loongarch_relax_frag (asection *, struct frag *, long);
 #define TARGET_USE_CFIPOP 1
 #define DWARF2_DEFAULT_RETURN_COLUMN 1 /* $ra.  */
 #define DWARF2_CIE_DATA_ALIGNMENT -4
-extern int loongarch_dwarf2_addr_size (void);
-#define DWARF2_FDE_RELOC_SIZE loongarch_dwarf2_addr_size ()
-#define DWARF2_ADDR_SIZE(bfd) loongarch_dwarf2_addr_size ()
-#define CFI_DIFF_EXPR_OK 0
 
 #define tc_cfi_frame_initial_instructions	\
   loongarch_cfi_frame_initial_instructions
-- 
2.31.1


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

* [PATCH v2 4/6] LoongArch: Move ifunc info to rela.dyn from rela.plt.
  2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
                   ` (2 preceding siblings ...)
  2022-07-21  1:37 ` [PATCH v2 3/6] LoongArch: gas: " liuzhensong
@ 2022-07-21  1:37 ` liuzhensong
  2022-07-21  1:37 ` [PATCH v2 5/6] bfd: Delete R_LARCH_NONE from dyn info of LoongArch liuzhensong
  4 siblings, 0 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

  Delete R_LARCH_IRELATIVE from dynamic loader (glibc ld.so) when
  loading lazy function (rela.plt section).

  In dynamic programes, move ifunc dynamic relocate info to section
  srelgot from srelplt.

  bfd/
    elfnn-loongarch.c
---
 bfd/elfnn-loongarch.c | 373 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 344 insertions(+), 29 deletions(-)

diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 21710dcb3fb..3d86e1422af 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -1207,6 +1207,259 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   return true;
 }
 
+/* A modified version of _bfd_elf_allocate_ifunc_dyn_relocs.
+   For local def and ref ifunc,
+   dynamic relocations are stored in
+   1.  rela.srelgot section in dynamic object (dll or exec).
+   2.  rela.irelplt section in static executable.
+   Unlike _bfd_elf_allocate_ifunc_dyn_relocs, rela.srelgot is used
+   instead of rela.srelplt.  Glibc ELF loader will not support
+   R_LARCH_IRELATIVE relocation in rela.plt.  */
+
+static bool
+local_allocate_ifunc_dyn_relocs (struct bfd_link_info *info,
+				    struct elf_link_hash_entry *h,
+				    struct elf_dyn_relocs **head,
+				    unsigned int plt_entry_size,
+				    unsigned int plt_header_size,
+				    unsigned int got_entry_size,
+				    bool avoid_plt)
+{
+  asection *plt, *gotplt, *relplt;
+  struct elf_dyn_relocs *p;
+  unsigned int sizeof_reloc;
+  const struct elf_backend_data *bed;
+  struct elf_link_hash_table *htab;
+  /* If AVOID_PLT is TRUE, don't use PLT if possible.  */
+  bool use_plt = !avoid_plt || h->plt.refcount > 0;
+  bool need_dynreloc = !use_plt || bfd_link_pic (info);
+
+  /* When a PIC object references a STT_GNU_IFUNC symbol defined
+     in executable or it isn't referenced via PLT, the address of
+     the resolved function may be used.  But in non-PIC executable,
+     the address of its plt slot may be used.  Pointer equality may
+     not work correctly.  PIE or non-PLT reference should be used if
+     pointer equality is required here.
+
+     If STT_GNU_IFUNC symbol is defined in position-dependent executable,
+     backend should change it to the normal function and set its address
+     to its PLT entry which should be resolved by R_*_IRELATIVE at
+     run-time.  All external references should be resolved to its PLT in
+     executable.  */
+  if (!need_dynreloc
+      && !(bfd_link_pde (info) && h->def_regular)
+      && (h->dynindx != -1
+	  || info->export_dynamic)
+      && h->pointer_equality_needed)
+    {
+      info->callbacks->einfo
+	/* xgettext:c-format.  */
+	(_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer "
+	   "equality in `%pB' can not be used when making an "
+	   "executable; recompile with -fPIE and relink with -pie\n"),
+	 h->root.root.string,
+	 h->root.u.def.section->owner);
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  htab = elf_hash_table (info);
+
+  /* When the symbol is marked with regular reference, if PLT isn't used
+     or we are building a PIC object, we must keep dynamic relocation
+     if there is non-GOT reference and use PLT if there is PC-relative
+     reference.  */
+  if (need_dynreloc && h->ref_regular)
+    {
+      bool keep = false;
+      for (p = *head; p != NULL; p = p->next)
+	if (p->count)
+	  {
+	    h->non_got_ref = 1;
+	    /* Need dynamic relocations for non-GOT reference.  */
+	    keep = true;
+	    if (p->pc_count)
+	      {
+		/* Must use PLT for PC-relative reference.  */
+		use_plt = true;
+		need_dynreloc = bfd_link_pic (info);
+		break;
+	      }
+	  }
+      if (keep)
+	goto keep;
+    }
+
+  /* Support garbage collection against STT_GNU_IFUNC symbols.  */
+  if (h->plt.refcount <= 0 && h->got.refcount <= 0)
+    {
+      h->got = htab->init_got_offset;
+      h->plt = htab->init_plt_offset;
+      *head = NULL;
+      return true;
+    }
+
+  /* Return and discard space for dynamic relocations against it if
+     it is never referenced.  */
+  if (!h->ref_regular)
+    {
+      if (h->plt.refcount > 0
+	  || h->got.refcount > 0)
+	abort ();
+      h->got = htab->init_got_offset;
+      h->plt = htab->init_plt_offset;
+      *head = NULL;
+      return true;
+    }
+
+ keep:
+  bed = get_elf_backend_data (info->output_bfd);
+  if (bed->rela_plts_and_copies_p)
+    sizeof_reloc = bed->s->sizeof_rela;
+  else
+    sizeof_reloc = bed->s->sizeof_rel;
+
+  /* When building a static executable, use iplt, igot.plt and
+     rela.iplt sections for STT_GNU_IFUNC symbols.  */
+  if (htab->splt != NULL)
+    {
+      plt = htab->splt;
+      gotplt = htab->sgotplt;
+      /* Change dynamic info of ifunc gotplt from srelplt to srelgot.  */
+      relplt = htab->srelgot;
+
+      /* If this is the first plt entry and PLT is used, make room for
+	 the special first entry.  */
+      if (plt->size == 0 && use_plt)
+	plt->size += plt_header_size;
+    }
+  else
+    {
+      plt = htab->iplt;
+      gotplt = htab->igotplt;
+      relplt = htab->irelplt;
+    }
+
+  if (use_plt)
+    {
+      /* Don't update value of STT_GNU_IFUNC symbol to PLT.  We need
+	 the original value for R_*_IRELATIVE.  */
+      h->plt.offset = plt->size;
+
+      /* Make room for this entry in the plt/iplt section.  */
+      plt->size += plt_entry_size;
+
+      /* We also need to make an entry in the got.plt/got.iplt section,
+	 which will be placed in the got section by the linker script.  */
+      gotplt->size += got_entry_size;
+    }
+
+  /* We also need to make an entry in the rela.plt/.rela.iplt
+     section for GOTPLT relocation if PLT is used.  */
+  if (use_plt)
+    {
+      relplt->size += sizeof_reloc;
+      relplt->reloc_count++;
+    }
+
+  /* We need dynamic relocation for STT_GNU_IFUNC symbol only when
+     there is a non-GOT reference in a PIC object or PLT isn't used.  */
+  if (!need_dynreloc || !h->non_got_ref)
+    *head = NULL;
+
+  /* Finally, allocate space.  */
+  p = *head;
+  if (p != NULL)
+    {
+      bfd_size_type count = 0;
+      do
+	{
+	  count += p->count;
+	  p = p->next;
+	}
+      while (p != NULL);
+
+      htab->ifunc_resolvers = count != 0;
+
+      /* Dynamic relocations are stored in
+	 1.  rela.srelgot section in PIC object.
+	 2.  rela.srelgot section in dynamic executable.
+	 3.  rela.irelplt section in static executable.  */
+      if (htab->splt != NULL)
+	htab->srelgot->size += count * sizeof_reloc;
+      else
+	{
+	  relplt->size += count * sizeof_reloc;
+	  relplt->reloc_count += count;
+	}
+    }
+
+  /* For STT_GNU_IFUNC symbol, got.plt has the real function address
+     and got has the PLT entry adddress.  We will load the GOT entry
+     with the PLT entry in finish_dynamic_symbol if it is used.  For
+     branch, it uses got.plt.  For symbol value, if PLT is used,
+     1.  Use got.plt in a PIC object if it is forced local or not
+     dynamic.
+     2.  Use got.plt in a non-PIC object if pointer equality isn't
+     needed.
+     3.  Use got.plt in PIE.
+     4.  Use got.plt if got isn't used.
+     5.  Otherwise use got so that it can be shared among different
+     objects at run-time.
+     If PLT isn't used, always use got for symbol value.
+     We only need to relocate got entry in PIC object or in dynamic
+     executable without PLT.  */
+  if (use_plt
+      && (h->got.refcount <= 0
+	  || (bfd_link_pic (info)
+	      && (h->dynindx == -1
+		  || h->forced_local))
+	  || (
+	      !h->pointer_equality_needed)
+	  || htab->sgot == NULL))
+    {
+      /* Use got.plt.  */
+      h->got.offset = (bfd_vma) -1;
+    }
+  else
+    {
+      if (!use_plt)
+	{
+	  /* PLT isn't used.  */
+	  h->plt.offset = (bfd_vma) -1;
+	}
+      if (h->got.refcount <= 0)
+	{
+	  /* GOT isn't need when there are only relocations for static
+	     pointers.  */
+	  h->got.offset = (bfd_vma) -1;
+	}
+      else
+	{
+	  h->got.offset = htab->sgot->size;
+	  htab->sgot->size += got_entry_size;
+	  /* Need to relocate the GOT entry in a PIC object or PLT isn't
+	     used.  Otherwise, the GOT entry will be filled with the PLT
+	     entry and dynamic GOT relocation isn't needed.  */
+	  if (need_dynreloc)
+	    {
+	      /* For non-static executable, dynamic GOT relocation is in
+		 rela.got section, but for static executable, it is
+		 in rela.iplt section.  */
+	      if (htab->splt != NULL)
+		htab->srelgot->size += sizeof_reloc;
+	      else
+		{
+		  relplt->size += sizeof_reloc;
+		  relplt->reloc_count++;
+		}
+	    }
+	}
+    }
+
+  return true;
+}
+
 /* Allocate space in .plt, .got and associated reloc sections for
    ifunc dynamic relocs.  */
 
@@ -1234,12 +1487,22 @@ elfNN_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
      here if it is defined and referenced in a non-shared object.  */
   if (h->type == STT_GNU_IFUNC && h->def_regular)
-    return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
-					       &h->dyn_relocs,
-					       PLT_ENTRY_SIZE,
-					       PLT_HEADER_SIZE,
-					       GOT_ENTRY_SIZE,
-					       false);
+    {
+      if (SYMBOL_REFERENCES_LOCAL (info, h))
+	return local_allocate_ifunc_dyn_relocs (info, h,
+						&h->dyn_relocs,
+						PLT_ENTRY_SIZE,
+						PLT_HEADER_SIZE,
+						GOT_ENTRY_SIZE,
+						false);
+      else
+	return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
+						   &h->dyn_relocs,
+						   PLT_ENTRY_SIZE,
+						   PLT_HEADER_SIZE,
+						   GOT_ENTRY_SIZE,
+						   false);
+    }
 
   return true;
 }
@@ -2162,22 +2425,40 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	      outrel.r_offset += sec_addr (input_section);
 
-	      /* A pointer point to a local ifunc symbol.  */
-	      if(h
-		 && h->type == STT_GNU_IFUNC
-		 && (h->dynindx == -1
-		     || h->forced_local
-		     || bfd_link_executable(info)))
+	      /* A pointer point to a ifunc symbol.  */
+	      if (h && h->type == STT_GNU_IFUNC)
 		{
-		  outrel.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
-		  outrel.r_addend = (h->root.u.def.value
-				     + h->root.u.def.section->output_section->vma
-				     + h->root.u.def.section->output_offset);
+		  if (h->dynindx == -1)
+		    {
+		      outrel.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
+		      outrel.r_addend = (h->root.u.def.value
+				  + h->root.u.def.section->output_section->vma
+				  + h->root.u.def.section->output_offset);
+		    }
+		  else
+		    {
+		      outrel.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_NN);
+		      outrel.r_addend = 0;
+		    }
 
-		  if (htab->elf.splt != NULL)
-		    sreloc = htab->elf.srelplt;
+		  if (SYMBOL_REFERENCES_LOCAL (info, h))
+		    {
+
+		      if (htab->elf.splt != NULL)
+			sreloc = htab->elf.srelgot;
+		      else
+			sreloc = htab->elf.irelplt;
+		    }
 		  else
-		    sreloc = htab->elf.irelplt;
+		    {
+
+		      if (bfd_link_pic (info))
+			sreloc = htab->elf.irelifunc;
+		      else if (htab->elf.splt != NULL)
+			sreloc = htab->elf.srelgot;
+		      else
+			sreloc = htab->elf.irelplt;
+		    }
 		}
 	      else if (resolved_dynly)
 		{
@@ -2816,10 +3097,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 	case R_LARCH_PCALA64_LO20:
 	case R_LARCH_PCALA64_HI12:
 	  if (h && h->plt.offset != MINUS_ONE)
-	    {
-	      BFD_ASSERT (rel->r_addend == 0);
-	      relocation = sec_addr (plt) + h->plt.offset;
-	    }
+	    relocation = sec_addr (plt) + h->plt.offset;
 	  else
 	    relocation += rel->r_addend;
 
@@ -3237,7 +3515,10 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
 
 	  plt = htab->elf.splt;
 	  gotplt = htab->elf.sgotplt;
-	  relplt = htab->elf.srelplt;
+	  if (h->type == STT_GNU_IFUNC && SYMBOL_REFERENCES_LOCAL (info, h))
+	    relplt = htab->elf.srelgot;
+	  else
+	    relplt = htab->elf.srelplt;
 	  plt_idx = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
 	  got_address =
 	    sec_addr (gotplt) + GOTPLT_HEADER_SIZE + plt_idx * GOT_ENTRY_SIZE;
@@ -3272,11 +3553,45 @@ loongarch_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       rela.r_offset = got_address;
 
-      /* Fill in the entry in the rela.plt section.  */
-      rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT);
-      rela.r_addend = 0;
-      loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
-      bed->s->swap_reloca_out (output_bfd, &rela, loc);
+      /* TRUE if this is a PLT reference to a local IFUNC.  */
+      if (PLT_LOCAL_IFUNC_P (info, h)
+	  && (relplt == htab->elf.srelgot
+	      || relplt == htab->elf.irelplt))
+	{
+	    {
+	      rela.r_info = ELFNN_R_INFO (0, R_LARCH_IRELATIVE);
+	      rela.r_addend = (h->root.u.def.value
+			       + h->root.u.def.section->output_section->vma
+			       + h->root.u.def.section->output_offset);
+	    }
+
+	    /* Find the space after dyn sort.  */
+	    {
+	      Elf_Internal_Rela *dyn = (Elf_Internal_Rela *)relplt->contents;
+	      bool fill = false;
+	      for (;dyn < dyn + relplt->size / sizeof (*dyn); dyn++)
+		{
+		  if (0 == dyn->r_offset)
+		    {
+		      bed->s->swap_reloca_out (output_bfd, &rela,
+					       (bfd_byte *)dyn);
+		      relplt->reloc_count++;
+		      fill = true;
+		      break;
+		    }
+		}
+	      BFD_ASSERT (fill);
+	    }
+
+	}
+      else
+	{
+	  /* Fill in the entry in the rela.plt section.  */
+	  rela.r_info = ELFNN_R_INFO (h->dynindx, R_LARCH_JUMP_SLOT);
+	  rela.r_addend = 0;
+	  loc = relplt->contents + plt_idx * sizeof (ElfNN_External_Rela);
+	  bed->s->swap_reloca_out (output_bfd, &rela, loc);
+	}
 
       if (!h->def_regular)
 	{
-- 
2.31.1


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

* [PATCH v2 5/6] bfd: Delete R_LARCH_NONE from dyn info of LoongArch.
  2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
                   ` (3 preceding siblings ...)
  2022-07-21  1:37 ` [PATCH v2 4/6] LoongArch: Move ifunc info to rela.dyn from rela.plt liuzhensong
@ 2022-07-21  1:37 ` liuzhensong
  4 siblings, 0 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  1:37 UTC (permalink / raw)
  To: binutils
  Cc: xry111, i.swmail, maskray, xuchenghua, mengqinggang, huangpei,
	chenglulu, caiyinyu, liuzhensong

  Some R_LARCH_64 in section .eh_frame will to generate
  R_LARCH_NONE, we change relocation to R_LARCH_32_PCREL
  from R_LARCH_64 in setction .eh_frame and not generate
  dynamic relocation for R_LARCH_32_PCREL.

  Add New relocate type R_LARCH_32_PCREL for .eh_frame.

  include/elf/
    loongarch.h

  bfd/
    bfd/bfd-in2.h
    libbfd.h
    reloc.c
    elfxx-loongarch.c
    elfnn-loongarch.c

  gas/config/
    tc-loongarch.c

  binutils/
    readelf.c

  ld/testsuite/ld-elf/
    eh5.d
---
 bfd/bfd-in2.h             |  1 +
 bfd/elfnn-loongarch.c     | 11 +++++++++++
 bfd/elfxx-loongarch.c     | 19 ++++++++++++++++++-
 bfd/libbfd.h              |  1 +
 bfd/reloc.c               |  3 +++
 binutils/readelf.c        |  2 ++
 include/elf/loongarch.h   |  5 ++++-
 ld/testsuite/ld-elf/eh5.d |  2 +-
 8 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 91e6ad76b9a..2d6e1bbc0b0 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -6309,6 +6309,7 @@ assembler and not (currently) written to any object files.  */
   BFD_RELOC_LARCH_TLS_LD_HI20,
   BFD_RELOC_LARCH_TLS_GD_PC_HI20,
   BFD_RELOC_LARCH_TLS_GD_HI20,
+  BFD_RELOC_LARCH_32_PCREL,
   BFD_RELOC_LARCH_RELAX,
   BFD_RELOC_UNUSED };
 
diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 3d86e1422af..4efe3d9370c 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -2010,6 +2010,17 @@ perform_relocation (const Elf_Internal_Rela *rel, asection *input_section,
       bfd_put (bits, input_bfd, opr1 - value, contents + rel->r_offset);
       break;
 
+    /* For eh_frame and debug info.  */
+    case R_LARCH_32_PCREL:
+      value -= sec_addr (input_section) + rel->r_offset;
+      value += rel->r_addend;
+      bfd_vma word = bfd_get (howto->bitsize, input_bfd,
+			      contents + rel->r_offset);
+      word = (word & ~howto->dst_mask) | (value & howto->dst_mask);
+      bfd_put (howto->bitsize, input_bfd, word, contents + rel->r_offset);
+      r = bfd_reloc_ok;
+      break;
+
     /* New reloc type.
        R_LARCH_B16 ~ R_LARCH_TLS_GD_HI20.  */
     case R_LARCH_B16:
diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c
index f059f1a0c05..0b00df556b7 100644
--- a/bfd/elfxx-loongarch.c
+++ b/bfd/elfxx-loongarch.c
@@ -1327,7 +1327,24 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 reloc_bits,				/* adjust_reloc_bits */
 	 "gd_hi20"),				/* larch_reloc_type_name */
 
-  LOONGARCH_HOWTO (R_LARCH_RELAX,		/* type (99).  */
+  LOONGARCH_HOWTO (R_LARCH_32_PCREL,		/* type (99).  */
+	 0,					/* rightshift.  */
+	 4,					/* size.  */
+	 32,					/* bitsize.  */
+	 true,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_dont,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_32_PCREL",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask */
+	 0xffffffff,				/* dst_mask */
+	 false,					/* pcrel_offset */
+	 BFD_RELOC_LARCH_32_PCREL,		/* bfd_reloc_code_real_type */
+	 NULL,					/* adjust_reloc_bits */
+	 NULL),					/* larch_reloc_type_name */
+
+  LOONGARCH_HOWTO (R_LARCH_RELAX,		/* type (100).  */
 	 0,					/* rightshift */
 	 1,					/* size */
 	 0,					/* bitsize */
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index 7eaa75ad11e..c766722efb0 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3492,6 +3492,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_LARCH_TLS_LD_HI20",
   "BFD_RELOC_LARCH_TLS_GD_PC_HI20",
   "BFD_RELOC_LARCH_TLS_GD_HI20",
+  "BFD_RELOC_LARCH_32_PCREL",
   "BFD_RELOC_LARCH_RELAX",
  "@@overflow: BFD_RELOC_UNUSED@@",
 };
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 59c2aaa0d31..268e4b13049 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8304,6 +8304,9 @@ ENUMX
 ENUMX
   BFD_RELOC_LARCH_TLS_GD_HI20
 
+ENUMX
+  BFD_RELOC_LARCH_32_PCREL
+
 ENUMX
   BFD_RELOC_LARCH_RELAX
 
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 6b7692d9dd5..4a3e448e30d 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -14389,6 +14389,8 @@ is_32bit_pcrel_reloc (Filedata * filedata, unsigned int reloc_type)
     case EM_AVR_OLD:
     case EM_AVR:
       return reloc_type == 36; /* R_AVR_32_PCREL.  */
+    case EM_LOONGARCH:
+      return reloc_type == 99;  /* R_LARCH_32_PCREL.  */
     case EM_MICROBLAZE:
       return reloc_type == 2;  /* R_MICROBLAZE_32_PCREL.  */
     case EM_OR1K:
diff --git a/include/elf/loongarch.h b/include/elf/loongarch.h
index 2d2bb6a5161..74757b82ca8 100644
--- a/include/elf/loongarch.h
+++ b/include/elf/loongarch.h
@@ -223,8 +223,11 @@ RELOC_NUMBER (R_LARCH_TLS_GD_PC_HI20, 97)
    %gd_hi20 (sym).  */
 RELOC_NUMBER (R_LARCH_TLS_GD_HI20, 98)
 
+/* For eh_frame and debug info.  */
+RELOC_NUMBER (R_LARCH_32_PCREL, 99)
+
 /* RELAX.  */
-RELOC_NUMBER (R_LARCH_RELAX, 99)
+RELOC_NUMBER (R_LARCH_RELAX, 100)
 
 END_RELOC_NUMBERS (R_LARCH_count)
 
diff --git a/ld/testsuite/ld-elf/eh5.d b/ld/testsuite/ld-elf/eh5.d
index d614251587c..4a697482355 100644
--- a/ld/testsuite/ld-elf/eh5.d
+++ b/ld/testsuite/ld-elf/eh5.d
@@ -4,7 +4,7 @@
 #ld:
 #readelf: -wf
 #target: [check_as_cfi]
-#xfail: alpha-*-*ecoff hppa64-*-* tile*-*-* visium-*-* loongarch64-*-*
+#xfail: alpha-*-*ecoff hppa64-*-* tile*-*-* visium-*-*
 
 Contents of the .eh_frame section:
 
-- 
2.31.1


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

* Re: [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types.
  2022-07-21  1:37 ` [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types liuzhensong
@ 2022-07-21  2:30   ` Xi Ruoyao
  2022-07-21  6:02     ` liuzhensong
  0 siblings, 1 reply; 10+ messages in thread
From: Xi Ruoyao @ 2022-07-21  2:30 UTC (permalink / raw)
  To: liuzhensong, binutils
  Cc: i.swmail, maskray, xuchenghua, mengqinggang, huangpei, chenglulu,
	caiyinyu

On Thu, 2022-07-21 at 09:37 +0800, liuzhensong wrote:
> +#define INSN_LA_ABS64                  \
> +  "lu12i.w %1,%%abs_hi20(%2);"         \
> +  "ori %1,%1,%%abs_lo12(%2);"          \
> +  "lu32i.d %1,%%abs64_lo20(%2);"       \
> +  "lu52i.d %1,%1,%%abs64_hi12(%2);",   \
> +  &LARCH_opts.ase_lp64, 0

Is it possible to use

lu12i.w %1, %%abs_hi20(%2)
lu32i.d %1, %%abs_lo12(%2)
lu52i.d %1, %1, %%abs64_hi12(%2)
addi.d  %1, %1, %%abs64_lo12(%2)

so a future linker relaxation (or some optimization, anyway) may combine
the last addi.d instruction with a following ld instruction?  Like

la.abs64 $t0, foo
ld.d     $t0, $t0, 0

=>

lu12i.w $t0, %abs_hi20(foo)
lu32i.d $t0, %abs_lo12(foo)
lu52i.d $t0, $t0, %abs64_hi12(foo)
ld.d    $t0, $t0, %abs64_lo12(foo)

(Doing so will need to take the highest bit of lo12 as a carry into the
higher 52 bits.)

-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

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

* Re: [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types.
  2022-07-21  2:30   ` Xi Ruoyao
@ 2022-07-21  6:02     ` liuzhensong
  2022-07-21  7:51       ` Xi Ruoyao
  0 siblings, 1 reply; 10+ messages in thread
From: liuzhensong @ 2022-07-21  6:02 UTC (permalink / raw)
  To: Xi Ruoyao, binutils
  Cc: i.swmail, maskray, xuchenghua, mengqinggang, huangpei, chenglulu,
	caiyinyu


在 2022/7/21 上午10:30, Xi Ruoyao 写道:
> On Thu, 2022-07-21 at 09:37 +0800, liuzhensong wrote:
>> +#define INSN_LA_ABS64                  \
>> +  "lu12i.w %1,%%abs_hi20(%2);"         \
>> +  "ori %1,%1,%%abs_lo12(%2);"          \
>> +  "lu32i.d %1,%%abs64_lo20(%2);"       \
>> +  "lu52i.d %1,%1,%%abs64_hi12(%2);",   \
>> +  &LARCH_opts.ase_lp64, 0
> Is it possible to use
>
> lu12i.w %1, %%abs_hi20(%2)
> lu32i.d %1, %%abs_lo12(%2)
> lu52i.d %1, %1, %%abs64_hi12(%2)
> addi.d  %1, %1, %%abs64_lo12(%2)
>
> so a future linker relaxation (or some optimization, anyway) may combine
> the last addi.d instruction with a following ld instruction?  Like
>
> la.abs64 $t0, foo
> ld.d     $t0, $t0, 0
>
> =>
>
> lu12i.w $t0, %abs_hi20(foo)
> lu32i.d $t0, %abs_lo12(foo)
> lu52i.d $t0, $t0, %abs64_hi12(foo)
> ld.d    $t0, $t0, %abs64_lo12(foo)
>
> (Doing so will need to take the highest bit of lo12 as a carry into the
> higher 52 bits.)
>
It seems default for addi.d/ld.d to access extreme address.

Simple example of abs32:

address range of abs32 : 0~0xffffffff

sym address: 0xfffff800

lui12.w $rd, 0xfffff

ori $rd, $d, 0x800

$rd is 0xfffff800

Obviously, lu12i.w and addi.w/ld.w can not get address > 0xfffff7fc abs64 is exactly same as abs32.
However, it is good idea to replace (lu12i.w,ori,ld.w) with (lu12i.w ld.w) if sym address in range -2G-2k  ~~  +2G-2k?





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

* Re: [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types.
  2022-07-21  6:02     ` liuzhensong
@ 2022-07-21  7:51       ` Xi Ruoyao
  2022-07-21  8:26         ` liuzhensong
  0 siblings, 1 reply; 10+ messages in thread
From: Xi Ruoyao @ 2022-07-21  7:51 UTC (permalink / raw)
  To: liuzhensong, binutils
  Cc: i.swmail, maskray, xuchenghua, mengqinggang, huangpei, chenglulu,
	caiyinyu

On Thu, 2022-07-21 at 14:02 +0800, liuzhensong wrote:

> 
> It seems default for addi.d/ld.d to access extreme address.
> Simple example of abs32:
> address range of abs32 :  0~0xffffffff
> sym address: 0xfffff800
> lui12.w $rd, 0xfffff
> ori $rd, $d, 0x800
> $rd is 0xfffff800
> Obviously, lu12i.w and addi.w/ld.w can not get address > 0xfffff7fc

Hmm... Wouldn't "addi.{w/d} $t0, $zero, -0x800" work for this?


-- 
Xi Ruoyao <xry111@xry111.site>
School of Aerospace Science and Technology, Xidian University

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

* Re: [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types.
  2022-07-21  7:51       ` Xi Ruoyao
@ 2022-07-21  8:26         ` liuzhensong
  0 siblings, 0 replies; 10+ messages in thread
From: liuzhensong @ 2022-07-21  8:26 UTC (permalink / raw)
  To: Xi Ruoyao, binutils
  Cc: i.swmail, maskray, xuchenghua, mengqinggang, huangpei, chenglulu,
	caiyinyu


在 2022/7/21 下午3:51, Xi Ruoyao 写道:
> On Thu, 2022-07-21 at 14:02 +0800, liuzhensong wrote:
>
>> It seems default for addi.d/ld.d to access extreme address.
>> Simple example of abs32:
>> address range of abs32 :  0~0xffffffff
>> sym address: 0xfffff800
>> lui12.w $rd, 0xfffff
>> ori $rd, $d, 0x800
>> $rd is 0xfffff800
>> Obviously, lu12i.w and addi.w/ld.w can not get address > 0xfffff7fc
> Hmm... Wouldn't "addi.{w/d} $t0, $zero, -0x800" work for this?
>
>
This can be do in ld relax rather than as.

+#define INSN_LA_ABS64                  \
+  "lu12i.w %1,%%abs_hi20(%2);"         \
+  "ori %1,%1,%%abs_lo12(%2);"          \
+  "lu32i.d %1,%%abs64_lo20(%2);"       \
+  "lu52i.d %1,%1,%%abs64_hi12(%2);",   \
+  &LARCH_opts.ase_lp64, 0
  


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

end of thread, other threads:[~2022-07-21  8:26 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-21  1:37 [PATCH v2 0/6] Add new relocations for LoongArch liuzhensong
2022-07-21  1:37 ` [PATCH v2 1/6] bfd: Add supported for LoongArch new relocations liuzhensong
2022-07-21  1:37 ` [PATCH v2 2/6] LoongArch:opcodes: Add new reloc types liuzhensong
2022-07-21  2:30   ` Xi Ruoyao
2022-07-21  6:02     ` liuzhensong
2022-07-21  7:51       ` Xi Ruoyao
2022-07-21  8:26         ` liuzhensong
2022-07-21  1:37 ` [PATCH v2 3/6] LoongArch: gas: " liuzhensong
2022-07-21  1:37 ` [PATCH v2 4/6] LoongArch: Move ifunc info to rela.dyn from rela.plt liuzhensong
2022-07-21  1:37 ` [PATCH v2 5/6] bfd: Delete R_LARCH_NONE from dyn info of LoongArch liuzhensong

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