public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v4] MIPS: Support PCREL GOT access
@ 2024-02-02 12:51 YunQiang Su
  2024-02-02 13:30 ` Maciej W. Rozycki
  0 siblings, 1 reply; 7+ messages in thread
From: YunQiang Su @ 2024-02-02 12:51 UTC (permalink / raw)
  To: nickc; +Cc: binutils, macro, xry111

Current if we need to access a GOT entry, we use the
got_base + offset. Thus we have GOT and XGOT.
From MIPSr6, we have PCREL instructions like ALUIPC,
so we have no need to use got_base now.
For pre-R6, we can use BAL to get the the value of PC.

In this patch, we add 8 new relocs:
	R_MIPS_GOTPC_HI16, R_MIPS_GOTPC_LO16,
	R_MIPS_GOTPC_CALL_HI16, R_MIPS_GOTPC_CALL_LO16,
	R_MIPS_GOTPC_AHI16, R_MIPS_GOTPC_ALO16,
	R_MIPS_GOTPC_CALL_AHI16, R_MIPS_GOTPC_CALL_ALO16.
These asm notes can be used for them:
	%gotpc_hi(sym), %gotpc_lo(sym),
	%gotpc_call_hi(sym), %gotpc_call_lo(sym),
	%gotpc_ahi(sym), %gotpc_alo(sym),
	%gotpc_call_ahi(sym), %gotpc_call_alo(sym).

3 new BFD_RELOCS are added for ALUIPC style relocs:
	BFD_RELOC_MIPS_ALO16_GOTOFF,
	BFD_RELOC_MIPS_AHI16_GOTOFF,
	BFD_RELOC_MIPS_AHI16_S_GOTOFF.

6 new BFD_RELOCS are added for function calling:
	BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
	BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
	BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
	BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
	BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
	BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,

The mapping between BFD_RELOC_ and R_MIPS_GOTPC are:
	BFD_RELOC_HI16_S_GOTOFF -> R_MIPS_GOTPC_HI16
	BFD_RELOC_LO16_GOTOFF -> R_MIPS_GOTPC_LO16
	BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_HI16
	BFD_RELOC_MIPS_LO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_LO16.
	BFD_RELOC_MIPS_AHI16_S_GOTOFF -> R_MIPS_GOTPC_AHI16
	BFD_RELOC_MIPS_ALO16_GOTOFF -> R_MIPS_GOTPC_ALO16
	BFD_RELOC_MIPS_MIPS_AHI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_AHI16
	BFD_RELOC_MIPS_MIPS_ALO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_ALO16.

The difference of BAL and ALUIPC is that ALUIPC will unset the lower 16
bits of result.

For r6, both styles are supported, and of course ALUIPC style is
recommended. For pre-R6, only BAL style is supported.

Here are the ASM examples:
ALUIPC:
	aluipc	$2,%gotpc_ahi(sym)
	lw	$2,%gotpc_alo(sym)($2)

BAL:
	bal	. + 8 # Note: $31 is clobbered here.
	lui	$2,%gotpc_hi(sym)
	addiu	$2,$2,$31
	lw	$2,%gotpc_lo(sym)($2)

gas/ChangeLog:
	* config/tc-mips.c: Add GOTPC relocs support.
	* testsuite/gas/mips/mips.exp: Add GOTPC testcases.
	* testsuite/gas/mips/gotpc-aluipc-32.s: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-64.s: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-n64.d: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-n32.d: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-o32.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-32.s: Ditto.
	* testsuite/gas/mips/gotpc-bal-64.s: Ditto.
	* testsuite/gas/mips/gotpc-bal-n64.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-n32.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-o32.d: Ditto.

bfd/ChangeLog:
	* elfxx-mips.c: Add GOTPC relocs support.
	* elf32-mips.c: Ditto.
	* elf64-mips.c: Ditto.
	* elfn32-mips.c: Ditto.
	* bfd-in2.h: Add new MIPS GOTPC BFD_RELOC defination.
	* reclos.c: Ditto.
	* libbfd.h: Ditto.

elfcpp/ChangeLog:
	* mips.h: Add new MIPS GOTPC relocs.

include/ChangeLog:
	* elf/mips.h (elf_mips_reloc_type): Add new MIPS GOTPC relocs.
---
 bfd/bfd-in2.h                             |  12 ++
 bfd/elf32-mips.c                          | 123 +++++++++++-
 bfd/elf64-mips.c                          | 234 +++++++++++++++++++++-
 bfd/elfn32-mips.c                         | 234 +++++++++++++++++++++-
 bfd/elfxx-mips.c                          | 107 +++++++++-
 bfd/libbfd.h                              |   9 +
 bfd/reloc.c                               |  18 ++
 elfcpp/mips.h                             |   8 +
 gas/config/tc-mips.c                      |  72 ++++++-
 gas/testsuite/gas/mips/gotpc-aluipc-32.s  |  51 +++++
 gas/testsuite/gas/mips/gotpc-aluipc-64.s  |  50 +++++
 gas/testsuite/gas/mips/gotpc-aluipc-n32.d |  17 ++
 gas/testsuite/gas/mips/gotpc-aluipc-n64.d |  25 +++
 gas/testsuite/gas/mips/gotpc-aluipc-o32.d |  17 ++
 gas/testsuite/gas/mips/gotpc-bal-32.s     |  55 +++++
 gas/testsuite/gas/mips/gotpc-bal-64.s     |  54 +++++
 gas/testsuite/gas/mips/gotpc-bal-n32.d    |  17 ++
 gas/testsuite/gas/mips/gotpc-bal-n64.d    |  25 +++
 gas/testsuite/gas/mips/gotpc-bal-o32.d    |  17 ++
 gas/testsuite/gas/mips/mips.exp           |   8 +
 include/elf/mips.h                        |  10 +-
 21 files changed, 1155 insertions(+), 8 deletions(-)
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-32.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-64.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n64.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-o32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-32.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-64.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n64.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-o32.d

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 581d8fe0b3e..bce2030f9c5 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3654,6 +3654,18 @@ enum bfd_reloc_code_real
   BFD_RELOC_MIPS_18_PCREL_S3,
   BFD_RELOC_MIPS_19_PCREL_S2,
 
+  BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
+
+  BFD_RELOC_MIPS_ALO16_GOTOFF,
+  BFD_RELOC_MIPS_AHI16_GOTOFF,
+  BFD_RELOC_MIPS_AHI16_S_GOTOFF,
+
+  BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,
+
   /* microMIPS versions of generic BFD relocs.  */
   BFD_RELOC_MICROMIPS_GPREL16,
   BFD_RELOC_MICROMIPS_HI16,
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index 63d7334dd52..7edcc30d331 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -809,6 +809,119 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
 	 0x0000ffff,		/* src_mask */
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The reloc used for BFD_RELOC_CTOR when doing a 64 bit link.  This
@@ -2030,7 +2143,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
index 489a461bb0b..dc4e56d1f55 100644
--- a/bfd/elf64-mips.c
+++ b/bfd/elf64-mips.c
@@ -889,6 +889,118 @@ static reloc_howto_type mips_elf64_howto_table_rel[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1670,6 +1782,118 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 static reloc_howto_type mips16_elf64_howto_table_rel[] =
@@ -3743,7 +3967,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c
index 7e672200006..03e17dfda64 100644
--- a/bfd/elfn32-mips.c
+++ b/bfd/elfn32-mips.c
@@ -868,6 +868,118 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1650,6 +1762,118 @@ static reloc_howto_type elf_mips_howto_table_rela[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 static reloc_howto_type elf_mips16_howto_table_rel[] =
@@ -3577,7 +3801,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 69dd71419ff..c8a9ceb7524 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -2267,19 +2267,28 @@ got_page_reloc_p (unsigned int r_type)
 static inline bool
 got_lo16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16;
+  return r_type == R_MIPS_GOT_LO16
+	 || r_type == R_MIPS_GOTPC_LO16
+	 || r_type == R_MIPS_GOTPC_ALO16
+	 || r_type == R_MICROMIPS_GOT_LO16;
 }
 
 static inline bool
 call_hi16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_CALL_HI16 || r_type == R_MICROMIPS_CALL_HI16;
+  return r_type == R_MIPS_CALL_HI16
+	 || r_type == R_MIPS_GOTPC_CALL_HI16
+	 || r_type == R_MIPS_GOTPC_CALL_AHI16
+	 || r_type == R_MICROMIPS_CALL_HI16;
 }
 
 static inline bool
 call_lo16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_CALL_LO16 || r_type == R_MICROMIPS_CALL_LO16;
+  return r_type == R_MIPS_CALL_LO16
+	 || r_type == R_MIPS_GOTPC_CALL_LO16
+	 || r_type == R_MIPS_GOTPC_CALL_ALO16
+	 || r_type == R_MICROMIPS_CALL_LO16;
 }
 
 static inline bool
@@ -5241,6 +5250,21 @@ mips_elf_highest (bfd_vma value ATTRIBUTE_UNUSED)
   return MINUS_ONE;
 #endif
 }
+
+/**/
+static bfd_vma
+mips_elf_16bit_align (bfd_vma value, bfd_vma p, bool hi)
+{
+  bfd_vma value_lo = (value & 0xffff) + (p & 0xffff);
+  bfd_vma value_hi = (value >> 16) & 0xffff;
+  value_hi += mips_elf_high (value_lo);
+  value_lo &= 0xffff;
+  if (hi)
+    return value_hi;
+  else
+    return value_lo;
+}
+
 \f
 /* Create the .compact_rel section.  */
 
@@ -5888,6 +5912,10 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS_GOT_DISP:
     case R_MIPS_GOT_LO16:
     case R_MIPS_CALL_LO16:
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_LO16:
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_ALO16:
     case R_MICROMIPS_CALL16:
     case R_MICROMIPS_GOT16:
     case R_MICROMIPS_GOT_PAGE:
@@ -5905,6 +5933,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       /* Fall through.  */
     case R_MIPS_GOT_HI16:
     case R_MIPS_CALL_HI16:
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_CALL_HI16:
     case R_MICROMIPS_GOT_HI16:
     case R_MICROMIPS_CALL_HI16:
       if (resolved_to_zero
@@ -5952,6 +5982,14 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS_CALL_HI16:
     case R_MIPS_GOT_LO16:
     case R_MIPS_CALL_LO16:
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_HI16:
+    case R_MIPS_GOTPC_CALL_LO16:
+    case R_MIPS_GOTPC_AHI16:
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_AHI16:
+    case R_MIPS_GOTPC_CALL_ALO16:
     case R_MICROMIPS_CALL16:
     case R_MICROMIPS_GOT16:
     case R_MICROMIPS_GOT_DISP:
@@ -6413,6 +6451,45 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       value &= howto->dst_mask;
       break;
 
+    /* For r6 and pre-r6, we can use:
+	bal	. + 8
+	lui	$2,%gotpc_hi(sym) # or %gotpc_call_hi
+	addu	$2,$2,$ra
+	lw	$2,%gotpc_lo(sym) # or %gotpc_call_lo
+       In this case, the LO should +=4, and HI should -=4.
+
+       For R6+, we can use
+	aluipc	$2,%gotpc_ahi(sym) # or %gotpc_call_ahi
+	lw	$2,%gotpc_alo(sym)($2) or %gotpc_call_alo
+       In this case, the HI is OK, while LO should +4 and add the page_offet */
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_CALL_HI16:
+      value = mips_elf_high (addend + gp - p + g - 4);
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_LO16:
+      if (howto->partial_inplace)
+	addend = _bfd_mips_elf_sign_extend (addend, 16);
+      value = addend + gp - p + g + 4;
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_AHI16:
+    case R_MIPS_GOTPC_CALL_AHI16:
+      value = mips_elf_16bit_align (addend + gp - p + g, p, true);
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_ALO16:
+      if (howto->partial_inplace)
+	addend = _bfd_mips_elf_sign_extend (addend, 16);
+      value = mips_elf_16bit_align (addend + gp - p + g, p, false);
+      value &= howto->dst_mask;
+      break;
+
     case R_MIPS_PCHI16:
       value = mips_elf_high (symbol + addend - p);
       value &= howto->dst_mask;
@@ -8282,6 +8359,14 @@ mips_elf_add_lo16_rel_addend (bfd *abfd,
     lo16_type = R_MIPS16_LO16;
   else if (micromips_reloc_p (r_type))
     lo16_type = R_MICROMIPS_LO16;
+  else if (r_type == R_MIPS_GOTPC_HI16)
+    lo16_type = R_MIPS_GOTPC_LO16;
+  else if (r_type == R_MIPS_GOTPC_CALL_HI16)
+    lo16_type = R_MIPS_GOTPC_CALL_LO16;
+  else if (r_type == R_MIPS_GOTPC_AHI16)
+    lo16_type = R_MIPS_GOTPC_ALO16;
+  else if (r_type == R_MIPS_GOTPC_CALL_AHI16)
+    lo16_type = R_MIPS_GOTPC_CALL_ALO16;
   else if (r_type == R_MIPS_PCHI16)
     lo16_type = R_MIPS_PCLO16;
   else
@@ -8742,6 +8827,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MIPS_CALL16:
 	case R_MIPS_CALL_HI16:
 	case R_MIPS_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_HI16:
+	case R_MIPS_GOTPC_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_AHI16:
+	case R_MIPS_GOTPC_CALL_ALO16:
 	case R_MIPS16_CALL16:
 	case R_MICROMIPS_CALL16:
 	case R_MICROMIPS_CALL_HI16:
@@ -8751,6 +8840,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_MIPS_GOT16:
 	case R_MIPS_GOT_LO16:
+	case R_MIPS_GOTPC_LO16:
+	case R_MIPS_GOTPC_ALO16:
 	case R_MIPS_GOT_PAGE:
 	case R_MIPS_GOT_DISP:
 	case R_MIPS16_GOT16:
@@ -8788,6 +8879,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	  /* Fall through.  */
 	case R_MIPS_GOT_HI16:
+	case R_MIPS_GOTPC_HI16:
+	case R_MIPS_GOTPC_AHI16:
 	case R_MIPS_GOT_OFST:
 	case R_MIPS_TLS_GOTTPREL:
 	case R_MIPS_TLS_GD:
@@ -8958,6 +9051,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_MIPS_CALL_HI16:
 	case R_MIPS_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_HI16:
+	case R_MIPS_GOTPC_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_AHI16:
+	case R_MIPS_GOTPC_CALL_ALO16:
 	case R_MICROMIPS_CALL_HI16:
 	case R_MICROMIPS_CALL_LO16:
 	  if (h != NULL)
@@ -8983,6 +9080,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MIPS_GOT16:
 	case R_MIPS_GOT_HI16:
 	case R_MIPS_GOT_LO16:
+	case R_MIPS_GOTPC_HI16:
+	case R_MIPS_GOTPC_LO16:
+	case R_MIPS_GOTPC_AHI16:
+	case R_MIPS_GOTPC_ALO16:
 	case R_MICROMIPS_GOT16:
 	case R_MICROMIPS_GOT_HI16:
 	case R_MICROMIPS_GOT_LO16:
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index ebd4f24149b..53604a094f6 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1272,6 +1272,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_MIPS_26_PCREL_S2",
   "BFD_RELOC_MIPS_18_PCREL_S3",
   "BFD_RELOC_MIPS_19_PCREL_S2",
+  "BFD_RELOC_MIPS_LO16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_HI16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_ALO16_GOTOFF",
+  "BFD_RELOC_MIPS_AHI16_GOTOFF",
+  "BFD_RELOC_MIPS_AHI16_S_GOTOFF",
+  "BFD_RELOC_MIPS_ALO16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_AHI16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL",
   "BFD_RELOC_MICROMIPS_GPREL16",
   "BFD_RELOC_MICROMIPS_HI16",
   "BFD_RELOC_MICROMIPS_HI16_S",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index e74cbd75e96..85e4081ccd6 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2040,6 +2040,24 @@ ENUMX
   BFD_RELOC_MIPS_18_PCREL_S3
 ENUMX
   BFD_RELOC_MIPS_19_PCREL_S2
+ENUMX
+  BFD_RELOC_LO16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_HI16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_HI16_S_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_ALO16_GOTOFF
+ENUMX
+  BFD_RELOC_AHI16_GOTOFF
+ENUMX
+  BFD_RELOC_AHI16_S_GOTOFF
+ENUMX
+  BFD_RELOC_ALO16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_AHI16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_AHI16_S_GOTOFF_CALL
 ENUMDOC
   MIPS PC-relative relocations.
 
diff --git a/elfcpp/mips.h b/elfcpp/mips.h
index e8a8e2458e9..79884bd651b 100644
--- a/elfcpp/mips.h
+++ b/elfcpp/mips.h
@@ -104,6 +104,14 @@ enum
   R_MIPS_PC19_S2 = 63,
   R_MIPS_PCHI16 = 64,
   R_MIPS_PCLO16 = 65,
+  R_MIPS_GOTPC_HI16 = 66,
+  R_MIPS_GOTPC_LO16 = 67,
+  R_MIPS_GOTPC_CALL_HI16 = 68,
+  R_MIPS_GOTPC_CALL_LO16 = 69,
+  R_MIPS_GOTPC_AHI16 = 70,
+  R_MIPS_GOTPC_ALO16 = 71,
+  R_MIPS_GOTPC_CALL_AHI16 = 72,
+  R_MIPS_GOTPC_CALL_ALO16 = 73,
   // These relocs are used for the mips16.
   R_MIPS16_26 = 100,
   R_MIPS16_GPREL = 101,
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 43c12de2c8a..1ec2feac072 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -4372,6 +4372,14 @@ limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
     case BFD_RELOC_32_PCREL:
     case BFD_RELOC_HI16_S_PCREL:
     case BFD_RELOC_LO16_PCREL:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
       return HAVE_64BIT_ADDRESSES;
 
     default:
@@ -7432,6 +7440,8 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
 
     case BFD_RELOC_HI16_S:
     case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
     case BFD_RELOC_MICROMIPS_HI16_S:
     case BFD_RELOC_MIPS16_HI16_S:
       *result = ((operand + 0x8000) >> 16) & 0xffff;
@@ -7445,6 +7455,10 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
 
     case BFD_RELOC_LO16:
     case BFD_RELOC_LO16_PCREL:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
     case BFD_RELOC_MICROMIPS_LO16:
     case BFD_RELOC_MIPS16_LO16:
       *result = operand & 0xffff;
@@ -7507,6 +7521,22 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
 	     (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
 
+  if (!ISA_IS_R6 (mips_opts.isa))
+    switch (*reloc_type)
+      {
+	default:
+	  break;
+
+	case BFD_RELOC_MIPS_ALO16_GOTOFF:
+	case BFD_RELOC_MIPS_AHI16_GOTOFF:
+	case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+	case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
+	case BFD_RELOC_MIPS_AHI16_GOTOFF_CALL:
+	case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+	  as_fatal (_("ALUIPC style GOTPC cannot work with pre-R6."));
+	  break;
+      }
+
   if (address_expr == NULL)
     ip->complete_p = 1;
   else if (reloc_type[0] <= BFD_RELOC_UNUSED
@@ -14583,7 +14613,15 @@ static const struct percent_op_match mips_percent_op[] =
   {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
   {"%hi", BFD_RELOC_HI16_S},
   {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
-  {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
+  {"%pcrel_lo", BFD_RELOC_LO16_PCREL},
+  {"%gotpc_hi", BFD_RELOC_HI16_S_GOTOFF},
+  {"%gotpc_lo", BFD_RELOC_LO16_GOTOFF},
+  {"%gotpc_call_hi", BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL},
+  {"%gotpc_call_lo", BFD_RELOC_MIPS_LO16_GOTOFF_CALL},
+  {"%gotpc_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF},
+  {"%gotpc_alo", BFD_RELOC_MIPS_ALO16_GOTOFF},
+  {"%gotpc_call_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL},
+  {"%gotpc_call_alo", BFD_RELOC_MIPS_ALO16_GOTOFF_CALL}
 };
 
 static const struct percent_op_match mips16_percent_op[] =
@@ -15541,6 +15579,14 @@ mips_force_relocation (fixS *fixp)
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+	  || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
 	  || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
 	  || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
     return 1;
@@ -15822,6 +15868,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       case BFD_RELOC_MIPS_19_PCREL_S2:
       case BFD_RELOC_HI16_S_PCREL:
       case BFD_RELOC_LO16_PCREL:
+      case BFD_RELOC_HI16_S_GOTOFF:
+      case BFD_RELOC_LO16_GOTOFF:
+      case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+      case BFD_RELOC_MIPS_ALO16_GOTOFF:
+      case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
 	break;
 
       case BFD_RELOC_32:
@@ -15965,6 +16019,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
     case BFD_RELOC_HI16_S_PCREL:
     case BFD_RELOC_LO16_PCREL:
     case BFD_RELOC_MIPS16_GPREL:
@@ -18386,6 +18448,14 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+		  || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
 		  || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
 		  || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
 
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-32.s b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
new file mode 100644
index 00000000000..877193313a1
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
@@ -0,0 +1,51 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	2008
+	.module	fp=xx
+	.module	nooddspreg
+	.module	arch=mips32r6
+	.abicalls
+	.text
+	.section	.rodata.str1.4,"aMS",@progbits,1
+	.align	2
+$LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,32,$31		# vars= 0, regs= 1/0, args= 16, gp= 8
+	.mask	0x80000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload	$25
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+
+	aluipc	$4,%gotpc_ahi($LC0)
+	lw	$4,%gotpc_alo($LC0)($4)
+
+	aluipc	$25,%gotpc_call_ahi(puts)
+	lw	$25,%gotpc_call_alo(puts)($25)
+
+	.cprestore	16
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	lw	$31,28($sp)
+	move	$2,$0
+	jr	$31
+	addiu	$sp,$sp,32
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (Debian 12.2.0-14) 12.2.0"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-64.s b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
new file mode 100644
index 00000000000..234be37b6fe
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
@@ -0,0 +1,50 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi64
+	.previous
+	.abicalls
+	.text
+	.section	.rodata.str1.8,"aMS",@progbits,1
+	.align	3
+.LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.align	3
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,16,$31		# vars= 0, regs= 2/0, args= 0, gp= 0
+	.mask	0x90000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	daddiu	$sp,$sp,-16
+	sd	$28,0($sp)
+	lui	$28,%hi(%neg(%gp_rel(main)))
+	daddu	$28,$28,$25
+	sd	$31,8($sp)
+	daddiu	$28,$28,%lo(%neg(%gp_rel(main)))
+
+	aluipc	$4,%gotpc_ahi(.LC0)
+	ld	$4,%gotpc_alo(.LC0)($4)
+
+	aluipc	$25,%gotpc_call_ahi(puts)
+	ld	$25,%gotpc_call_alo(puts)($25)
+
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	ld	$31,8($sp)
+	ld	$28,0($sp)
+	move	$2,$0
+	jr	$31
+	daddiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n32.d b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
new file mode 100644
index 00000000000..41bc6d474d7
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N32)
+#source: gotpc-aluipc-64.s
+#as: -n32 -mips64r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_ALO16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n64.d b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
new file mode 100644
index 00000000000..0561040ef09
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
@@ -0,0 +1,25 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N64)
+#source: gotpc-aluipc-64.s
+#as: -64 -mips64r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.LC0
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_ALO16	\.LC0
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-o32.d b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
new file mode 100644
index 00000000000..1832061a34e
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (OldABI, ALUIPC, O32)
+#source: gotpc-aluipc-32.s
+#as: -32 -mips32r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_ALO16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-32.s b/gas/testsuite/gas/mips/gotpc-bal-32.s
new file mode 100644
index 00000000000..841a13dbf87
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-32.s
@@ -0,0 +1,55 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	2008
+	.module	fp=xx
+	.module	nooddspreg
+	.module	arch=mips32r6
+	.abicalls
+	.text
+	.section	.rodata.str1.4,"aMS",@progbits,1
+	.align	2
+$LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,32,$31		# vars= 0, regs= 1/0, args= 16, gp= 8
+	.mask	0x80000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload	$25
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+
+	bal	. + 8
+	lui	$4,%gotpc_hi($LC0)
+	addu	$4,$4,$31
+	lw	$4,%gotpc_lo($LC0)($4)
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(puts)
+	addu	$25,$25,$31
+	lw	$25,%gotpc_call_lo(puts)($25)
+
+	.cprestore	16
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	lw	$31,28($sp)
+	move	$2,$0
+	jr	$31
+	addiu	$sp,$sp,32
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (Debian 12.2.0-14) 12.2.0"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-bal-64.s b/gas/testsuite/gas/mips/gotpc-bal-64.s
new file mode 100644
index 00000000000..c97e6b87af9
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-64.s
@@ -0,0 +1,54 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi64
+	.previous
+	.abicalls
+	.text
+	.section	.rodata.str1.8,"aMS",@progbits,1
+	.align	3
+.LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.align	3
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,16,$31		# vars= 0, regs= 2/0, args= 0, gp= 0
+	.mask	0x90000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	daddiu	$sp,$sp,-16
+	sd	$28,0($sp)
+	lui	$28,%hi(%neg(%gp_rel(main)))
+	daddu	$28,$28,$25
+	sd	$31,8($sp)
+	daddiu	$28,$28,%lo(%neg(%gp_rel(main)))
+
+	bal	. + 8
+	lui	$4,%gotpc_hi(.LC0)
+	daddu	$4,$4,$31
+	ld	$4,%gotpc_lo(.LC0)($4)
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(puts)
+	daddu	$25,$25,$31
+	ld	$25,%gotpc_call_lo(puts)($25)
+
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	ld	$31,8($sp)
+	ld	$28,0($sp)
+	move	$2,$0
+	jr	$31
+	daddiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-bal-n32.d b/gas/testsuite/gas/mips/gotpc-bal-n32.d
new file mode 100644
index 00000000000..ec412020dc7
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-n32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, BAL, N32)
+#source: gotpc-bal-64.s
+#as: -n32 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-n64.d b/gas/testsuite/gas/mips/gotpc-bal-n64.d
new file mode 100644
index 00000000000..60b9f020f50
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-n64.d
@@ -0,0 +1,25 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, BAL, N64)
+#source: gotpc-bal-64.s
+#as: -64 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.8
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.8
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-o32.d b/gas/testsuite/gas/mips/gotpc-bal-o32.d
new file mode 100644
index 00000000000..57cb891042a
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-o32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (OldABI, BAL, O32)
+#source: gotpc-bal-32.s
+#as: -32 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index 2ddbf0c768d..619d04b3bd5 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -1210,7 +1210,15 @@ if { [istarget mips*-*-vxworks*] } {
 	run_dump_test "elf-rel-xgot-n32"
 	run_dump_test "elf-rel-got-n64"
 	run_dump_test "elf-rel-xgot-n64"
+
+	run_dump_test "gotpc-bal-n64"
+	run_dump_test "gotpc-bal-n32"
+	run_dump_test "gotpc-aluipc-n64"
+	run_dump_test "gotpc-aluipc-n32"
     }
+    run_dump_test "gotpc-bal-o32"
+    run_dump_test "gotpc-aluipc-o32"
+
     run_dump_test "elf-rel17"
     if $has_newabi {
 	run_dump_test "elf-rel18"
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 686d5500e02..23e95fe0c45 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -98,7 +98,15 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
   RELOC_NUMBER (R_MIPS_PC19_S2, 63)
   RELOC_NUMBER (R_MIPS_PCHI16, 64)
   RELOC_NUMBER (R_MIPS_PCLO16, 65)
-  FAKE_RELOC (R_MIPS_max, 66)
+  RELOC_NUMBER (R_MIPS_GOTPC_HI16, 66)
+  RELOC_NUMBER (R_MIPS_GOTPC_LO16, 67)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_HI16, 68)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_LO16, 69)
+  RELOC_NUMBER (R_MIPS_GOTPC_AHI16, 70)
+  RELOC_NUMBER (R_MIPS_GOTPC_ALO16, 71)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_AHI16, 72)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_ALO16, 73)
+  FAKE_RELOC (R_MIPS_max, 74)
   /* These relocs are used for the mips16.  */
   FAKE_RELOC (R_MIPS16_min, 100)
   RELOC_NUMBER (R_MIPS16_26, 100)
-- 
2.39.2


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

* Re: [PATCH v4] MIPS: Support PCREL GOT access
  2024-02-02 12:51 [PATCH v4] MIPS: Support PCREL GOT access YunQiang Su
@ 2024-02-02 13:30 ` Maciej W. Rozycki
  2024-02-03  2:03   ` YunQiang Su
  0 siblings, 1 reply; 7+ messages in thread
From: Maciej W. Rozycki @ 2024-02-02 13:30 UTC (permalink / raw)
  To: YunQiang Su; +Cc: Nick Clifton, binutils, xry111

On Fri, 2 Feb 2024, YunQiang Su wrote:

> Current if we need to access a GOT entry, we use the
> got_base + offset. Thus we have GOT and XGOT.
> >From MIPSr6, we have PCREL instructions like ALUIPC,

 I'm assuming ">" above is a typo; overall the change description may need 
a little bit polishing, but let's leave it until all the issues with code 
itself have been sorted.

> so we have no need to use got_base now.
> For pre-R6, we can use BAL to get the the value of PC.

 I've skimmed over your change and spotted a couple of issues right away, 
as noted below.  I'll go through your change more thoroughly again.

 Have the new relocations been formally documented anywhere?

> diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
> index 489a461bb0b..dc4e56d1f55 100644
> --- a/bfd/elf64-mips.c
> +++ b/bfd/elf64-mips.c
> @@ -1670,6 +1782,118 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
>  	 0x0000ffff,		/* dst_mask */
>  	 true),			/* pcrel_offset */
>  
> +  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
> +	 16,			/* rightshift */
> +	 4,			/* size */
> +	 16,			/* bitsize */
> +	 true,			/* pc_relative */
> +	 0,			/* bitpos */
> +	 complain_overflow_signed, /* complain_on_overflow */
> +	 _bfd_mips_elf_generic_reloc,   /* special_function */
> +	 "R_MIPS_GOTPC_HI16",	/* name */
> +	 true,			/* partial_inplace */
> +	 0x0000ffff,		/* src_mask */
> +	 0x0000ffff,		/* dst_mask */
> +	 true),			/* pcrel_offset */

 For RELA relocations you need to set `partial_inplace' to `false' and 
`src_mask' to 0, because they do not store the addend in the field 
relocated.  Likewise throughout and with n32.

> diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
> index 69dd71419ff..c8a9ceb7524 100644
> --- a/bfd/elfxx-mips.c
> +++ b/bfd/elfxx-mips.c
> @@ -2267,19 +2267,28 @@ got_page_reloc_p (unsigned int r_type)
>  static inline bool
>  got_lo16_reloc_p (unsigned int r_type)
>  {
> -  return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16;
> +  return r_type == R_MIPS_GOT_LO16
> +	 || r_type == R_MIPS_GOTPC_LO16
> +	 || r_type == R_MIPS_GOTPC_ALO16
> +	 || r_type == R_MICROMIPS_GOT_LO16;
>  }

 Please follow the GNU coding style, i.e.:

  return (r_type == R_MIPS_GOT_LO16
	  || r_type == R_MIPS_GOTPC_LO16
	  || r_type == R_MIPS_GOTPC_ALO16
	  || r_type == R_MICROMIPS_GOT_LO16);

Likewise throughout.

  Maciej

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

* Re: [PATCH v4] MIPS: Support PCREL GOT access
  2024-02-02 13:30 ` Maciej W. Rozycki
@ 2024-02-03  2:03   ` YunQiang Su
  0 siblings, 0 replies; 7+ messages in thread
From: YunQiang Su @ 2024-02-03  2:03 UTC (permalink / raw)
  To: Maciej W. Rozycki; +Cc: Nick Clifton, binutils, xry111

Maciej W. Rozycki <macro@orcam.me.uk> 于2024年2月2日周五 21:30写道:
>
> On Fri, 2 Feb 2024, YunQiang Su wrote:
>
> > Current if we need to access a GOT entry, we use the
> > got_base + offset. Thus we have GOT and XGOT.
> > >From MIPSr6, we have PCREL instructions like ALUIPC,
>
>  I'm assuming ">" above is a typo; overall the change description may need
> a little bit polishing, but let's leave it until all the issues with code
> itself have been sorted.
>
> > so we have no need to use got_base now.
> > For pre-R6, we can use BAL to get the the value of PC.
>
>  I've skimmed over your change and spotted a couple of issues right away,
> as noted below.  I'll go through your change more thoroughly again.
>

Thanks.

>  Have the new relocations been formally documented anywhere?
>

Not yet. I wanted to put the documents in some subdirectory of binutils or gcc,
while it seems that these doc directories were used to hold the documents of
these projects only.

Is it a good idea to host the new document on Github or somewhere else?

For contents, I think that we should have a document for all GNU extensions.
Let's use the new relocs as the starting point.

> > diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
> > index 489a461bb0b..dc4e56d1f55 100644
> > --- a/bfd/elf64-mips.c
> > +++ b/bfd/elf64-mips.c
> > @@ -1670,6 +1782,118 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
> >        0x0000ffff,            /* dst_mask */
> >        true),                 /* pcrel_offset */
> >
> > +  HOWTO (R_MIPS_GOTPC_HI16,  /* type */
> > +      16,                    /* rightshift */
> > +      4,                     /* size */
> > +      16,                    /* bitsize */
> > +      true,                  /* pc_relative */
> > +      0,                     /* bitpos */
> > +      complain_overflow_signed, /* complain_on_overflow */
> > +      _bfd_mips_elf_generic_reloc,   /* special_function */
> > +      "R_MIPS_GOTPC_HI16",   /* name */
> > +      true,                  /* partial_inplace */
> > +      0x0000ffff,            /* src_mask */
> > +      0x0000ffff,            /* dst_mask */
> > +      true),                 /* pcrel_offset */
>
>  For RELA relocations you need to set `partial_inplace' to `false' and
> `src_mask' to 0, because they do not store the addend in the field
> relocated.  Likewise throughout and with n32.
>

Thanks. Fixed in my local repo. When you have more comments,
I will send V4.

> > diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
> > index 69dd71419ff..c8a9ceb7524 100644
> > --- a/bfd/elfxx-mips.c
> > +++ b/bfd/elfxx-mips.c
> > @@ -2267,19 +2267,28 @@ got_page_reloc_p (unsigned int r_type)
> >  static inline bool
> >  got_lo16_reloc_p (unsigned int r_type)
> >  {
> > -  return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16;
> > +  return r_type == R_MIPS_GOT_LO16
> > +      || r_type == R_MIPS_GOTPC_LO16
> > +      || r_type == R_MIPS_GOTPC_ALO16
> > +      || r_type == R_MICROMIPS_GOT_LO16;
> >  }
>
>  Please follow the GNU coding style, i.e.:
>
>   return (r_type == R_MIPS_GOT_LO16
>           || r_type == R_MIPS_GOTPC_LO16
>           || r_type == R_MIPS_GOTPC_ALO16
>           || r_type == R_MICROMIPS_GOT_LO16);
>
> Likewise throughout.
>

Ditto.

>   Maciej

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

* Re: [PATCH v4] MIPS: Support PCREL GOT access
       [not found] ` <DS7PR12MB57650A5401095ED70D3AB0EDCB3D2@DS7PR12MB5765.namprd12.prod.outlook.com>
@ 2024-04-18 10:56   ` YunQiang Su
  0 siblings, 0 replies; 7+ messages in thread
From: YunQiang Su @ 2024-04-18 10:56 UTC (permalink / raw)
  To: Fangrui Song; +Cc: nickc, binutils, macro, xry111

Fangrui Song <i@maskray.me> 于2024年4月3日周三 12:11写道:
>
> On Sat, Mar 16, 2024 at 1:51 AM YunQiang Su <syq@gcc.gnu.org> wrote:
> >
> > Current if we need to access a GOT entry, we use the
> > got_base + offset. Thus we have GOT and XGOT.
> > From MIPSr6, we have PCREL instructions like ALUIPC,
> > so we have no need to use got_base now.
> > For pre-R6, we can use BAL to get the the value of PC.
>
> Currently loading a GOT entry requires loading the GOT base address
> first, then adding an offset.
> Therefore, (elaborate the reason) we need both GOT and XGOT.
> MIPSr6 introduced PC-relative instructions like ..., which allow us to
> use a pair of .. and .. to materialize the GOT entry address.
>

Thanks.

Currently loading a GOT entry requires loading the GOT base address
first, then adding an offset. Therefore, (elaborate the reason)
we need both GOT and XGOT. MIPSr6 introduced PC-relative instructions
like ALUIPC/AUIPC, which allow us to use a pair of GOTPC_HI and
GOTPC_LO to materialize the GOT entry address.


> > In this patch, we add 8 new relocs:
> >         R_MIPS_GOTPC_HI16, R_MIPS_GOTPC_LO16,
> >         R_MIPS_GOTPC_CALL_HI16, R_MIPS_GOTPC_CALL_LO16,
> >         R_MIPS_GOTPC_AHI16, R_MIPS_GOTPC_ALO16,
> >         R_MIPS_GOTPC_CALL_AHI16, R_MIPS_GOTPC_CALL_ALO16.
> > These asm notes can be used for them:
> >         %gotpc_hi(sym), %gotpc_lo(sym),
> >         %gotpc_call_hi(sym), %gotpc_call_lo(sym),
> >         %gotpc_ahi(sym), %gotpc_alo(sym),
> >         %gotpc_call_ahi(sym), %gotpc_call_alo(sym).
>
> GCC's mips port names assembler relocation operators
> (gas/config/tc-mips.c names them `mips_percent_op`).
> Other ports seem to prefer "modifiers" (assembler modifiers,
> expression modifiers).
>

These percent_op can be used for them


> >         * bfd-in2.h: Add new MIPS GOTPC BFD_RELOC defination.
>
> typo
>
Thanks.

Ping  again.

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

* Re: [PATCH v4] MIPS: Support PCREL GOT access
  2024-03-16  8:51 YunQiang Su
  2024-03-26  2:18 ` YunQiang Su
@ 2024-04-03  4:04 ` Fangrui Song
       [not found] ` <DS7PR12MB57650A5401095ED70D3AB0EDCB3D2@DS7PR12MB5765.namprd12.prod.outlook.com>
  2 siblings, 0 replies; 7+ messages in thread
From: Fangrui Song @ 2024-04-03  4:04 UTC (permalink / raw)
  To: YunQiang Su; +Cc: nickc, binutils, macro, xry111

On Sat, Mar 16, 2024 at 1:51 AM YunQiang Su <syq@gcc.gnu.org> wrote:
>
> Current if we need to access a GOT entry, we use the
> got_base + offset. Thus we have GOT and XGOT.
> From MIPSr6, we have PCREL instructions like ALUIPC,
> so we have no need to use got_base now.
> For pre-R6, we can use BAL to get the the value of PC.

Currently loading a GOT entry requires loading the GOT base address
first, then adding an offset.
Therefore, (elaborate the reason) we need both GOT and XGOT.
MIPSr6 introduced PC-relative instructions like ..., which allow us to
use a pair of .. and .. to materialize the GOT entry address.

> In this patch, we add 8 new relocs:
>         R_MIPS_GOTPC_HI16, R_MIPS_GOTPC_LO16,
>         R_MIPS_GOTPC_CALL_HI16, R_MIPS_GOTPC_CALL_LO16,
>         R_MIPS_GOTPC_AHI16, R_MIPS_GOTPC_ALO16,
>         R_MIPS_GOTPC_CALL_AHI16, R_MIPS_GOTPC_CALL_ALO16.
> These asm notes can be used for them:
>         %gotpc_hi(sym), %gotpc_lo(sym),
>         %gotpc_call_hi(sym), %gotpc_call_lo(sym),
>         %gotpc_ahi(sym), %gotpc_alo(sym),
>         %gotpc_call_ahi(sym), %gotpc_call_alo(sym).

GCC's mips port names assembler relocation operators
(gas/config/tc-mips.c names them `mips_percent_op`).
Other ports seem to prefer "modifiers" (assembler modifiers,
expression modifiers).

> 3 new BFD_RELOCS are added for ALUIPC style relocs:
>         BFD_RELOC_MIPS_ALO16_GOTOFF,
>         BFD_RELOC_MIPS_AHI16_GOTOFF,
>         BFD_RELOC_MIPS_AHI16_S_GOTOFF.
>
> 6 new BFD_RELOCS are added for function calling:
>         BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
>         BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
>         BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
>         BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
>         BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
>         BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,
>
> The mapping between BFD_RELOC_ and R_MIPS_GOTPC are:
>         BFD_RELOC_HI16_S_GOTOFF -> R_MIPS_GOTPC_HI16
>         BFD_RELOC_LO16_GOTOFF -> R_MIPS_GOTPC_LO16
>         BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_HI16
>         BFD_RELOC_MIPS_LO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_LO16.
>         BFD_RELOC_MIPS_AHI16_S_GOTOFF -> R_MIPS_GOTPC_AHI16
>         BFD_RELOC_MIPS_ALO16_GOTOFF -> R_MIPS_GOTPC_ALO16
>         BFD_RELOC_MIPS_MIPS_AHI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_AHI16
>         BFD_RELOC_MIPS_MIPS_ALO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_ALO16.
>
> The difference of BAL and ALUIPC is that ALUIPC will unset the lower 16
> bits of result.
>
> For r6, both styles are supported, and of course ALUIPC style is
> recommended. For pre-R6, only BAL style is supported.
>
> Here are the ASM examples:
> ALUIPC:
>         aluipc  $2,%gotpc_ahi(sym)
>         lw      $2,%gotpc_alo(sym)($2)
>
> BAL:
>         bal     . + 8 # Note: $31 is clobbered here.
>         lui     $2,%gotpc_hi(sym)
>         addiu   $2,$2,$31
>         lw      $2,%gotpc_lo(sym)($2)
>
> gas/ChangeLog:
>         * config/tc-mips.c: Add GOTPC relocs support.
>         * testsuite/gas/mips/mips.exp: Add GOTPC testcases.
>         * testsuite/gas/mips/gotpc-aluipc-32.s: Ditto.
>         * testsuite/gas/mips/gotpc-aluipc-64.s: Ditto.
>         * testsuite/gas/mips/gotpc-aluipc-n64.d: Ditto.
>         * testsuite/gas/mips/gotpc-aluipc-n32.d: Ditto.
>         * testsuite/gas/mips/gotpc-aluipc-o32.d: Ditto.
>         * testsuite/gas/mips/gotpc-bal-32.s: Ditto.
>         * testsuite/gas/mips/gotpc-bal-64.s: Ditto.
>         * testsuite/gas/mips/gotpc-bal-n64.d: Ditto.
>         * testsuite/gas/mips/gotpc-bal-n32.d: Ditto.
>         * testsuite/gas/mips/gotpc-bal-o32.d: Ditto.
>
> bfd/ChangeLog:
>         * elfxx-mips.c: Add GOTPC relocs support.
>         * elf32-mips.c: Ditto.
>         * elf64-mips.c: Ditto.
>         * elfn32-mips.c: Ditto.
>         * bfd-in2.h: Add new MIPS GOTPC BFD_RELOC defination.

typo

>         * reclos.c: Ditto.
>         * libbfd.h: Ditto.
>
> elfcpp/ChangeLog:
>         * mips.h: Add new MIPS GOTPC relocs.
>
> include/ChangeLog:
>         * elf/mips.h (elf_mips_reloc_type): Add new MIPS GOTPC relocs.
>
> ld/ChangeLog:
>         * testsuite/ld-mips-elf/mips-elf.exp: Add GOTPC testcases.
>         * testsuite/ld-mips-elf/gotpc-callee.s: Likewise.
>         * testsuite/ld-mips-elf/gotpc.ld: Likewise.
>         * testsuite/ld-mips-elf/gotpc-o32.s: Likewise.
>         * testsuite/ld-mips-elf/gotpc-o32.dd: Likewise.
>         * testsuite/ld-mips-elf/gotpc-o32.gd: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n32.s: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n32.dd: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n32.gd: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n64.s: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n64.dd: Likewise.
>         * testsuite/ld-mips-elf/gotpc-n64.gd: Likewise.
> ---
>  bfd/bfd-in2.h                             |  12 ++
>  bfd/elf32-mips.c                          | 123 +++++++++++-
>  bfd/elf64-mips.c                          | 234 +++++++++++++++++++++-
>  bfd/elfn32-mips.c                         | 234 +++++++++++++++++++++-
>  bfd/elfxx-mips.c                          | 107 +++++++++-
>  bfd/libbfd.h                              |   9 +
>  bfd/reloc.c                               |  18 ++
>  elfcpp/mips.h                             |   8 +
>  gas/config/tc-mips.c                      |  72 ++++++-
>  gas/testsuite/gas/mips/gotpc-aluipc-32.s  |  51 +++++
>  gas/testsuite/gas/mips/gotpc-aluipc-64.s  |  50 +++++
>  gas/testsuite/gas/mips/gotpc-aluipc-n32.d |  17 ++
>  gas/testsuite/gas/mips/gotpc-aluipc-n64.d |  25 +++
>  gas/testsuite/gas/mips/gotpc-aluipc-o32.d |  17 ++
>  gas/testsuite/gas/mips/gotpc-bal-32.s     |  55 +++++
>  gas/testsuite/gas/mips/gotpc-bal-64.s     |  54 +++++
>  gas/testsuite/gas/mips/gotpc-bal-n32.d    |  17 ++
>  gas/testsuite/gas/mips/gotpc-bal-n64.d    |  25 +++
>  gas/testsuite/gas/mips/gotpc-bal-o32.d    |  17 ++
>  gas/testsuite/gas/mips/mips.exp           |   8 +
>  include/elf/mips.h                        |  10 +-
>  ld/testsuite/ld-mips-elf/gotpc-callee.s   |  41 ++++
>  ld/testsuite/ld-mips-elf/gotpc-n32.dd     |  73 +++++++
>  ld/testsuite/ld-mips-elf/gotpc-n32.gd     |  15 ++
>  ld/testsuite/ld-mips-elf/gotpc-n32.s      |  46 +++++
>  ld/testsuite/ld-mips-elf/gotpc-n64.dd     |  75 +++++++
>  ld/testsuite/ld-mips-elf/gotpc-n64.gd     |  15 ++
>  ld/testsuite/ld-mips-elf/gotpc-n64.s      |  46 +++++
>  ld/testsuite/ld-mips-elf/gotpc-o32.dd     |  71 +++++++
>  ld/testsuite/ld-mips-elf/gotpc-o32.gd     |  15 ++
>  ld/testsuite/ld-mips-elf/gotpc-o32.s      |  45 +++++
>  ld/testsuite/ld-mips-elf/gotpc.ld         |  17 ++
>  ld/testsuite/ld-mips-elf/mips-elf.exp     |  23 +++
>  33 files changed, 1637 insertions(+), 8 deletions(-)
>  create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-32.s
>  create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-64.s
>  create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n32.d
>  create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n64.d
>  create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-o32.d
>  create mode 100644 gas/testsuite/gas/mips/gotpc-bal-32.s
>  create mode 100644 gas/testsuite/gas/mips/gotpc-bal-64.s
>  create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n32.d
>  create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n64.d
>  create mode 100644 gas/testsuite/gas/mips/gotpc-bal-o32.d
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-callee.s
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.dd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.gd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.s
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.dd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.gd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.s
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.dd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.gd
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.s
>  create mode 100644 ld/testsuite/ld-mips-elf/gotpc.ld
>
> diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
> index 29602e054da..e8bb1195c27 100644
> --- a/bfd/bfd-in2.h
> +++ b/bfd/bfd-in2.h
> @@ -3658,6 +3658,18 @@ enum bfd_reloc_code_real
>    BFD_RELOC_MIPS_18_PCREL_S3,
>    BFD_RELOC_MIPS_19_PCREL_S2,
>
> +  BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
> +  BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
> +  BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
> +
> +  BFD_RELOC_MIPS_ALO16_GOTOFF,
> +  BFD_RELOC_MIPS_AHI16_GOTOFF,
> +  BFD_RELOC_MIPS_AHI16_S_GOTOFF,
> +
> +  BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
> +  BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
> +  BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,
> +
>    /* microMIPS versions of generic BFD relocs.  */
>    BFD_RELOC_MICROMIPS_GPREL16,
>    BFD_RELOC_MICROMIPS_HI16,
> diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
> index 63d7334dd52..7edcc30d331 100644
> --- a/bfd/elf32-mips.c
> +++ b/bfd/elf32-mips.c
> @@ -809,6 +809,119 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
>          0x0000ffff,            /* src_mask */
>          0x0000ffff,            /* dst_mask */
>          true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_HI16,    /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_HI16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_LO16,    /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_LO16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_HI16,       /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_HI16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_LO16,       /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_LO16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_AHI16,   /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_AHI16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_ALO16,   /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_ALO16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_AHI16,      /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_AHI16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_ALO16,      /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_ALO16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
>  };
>
>  /* The reloc used for BFD_RELOC_CTOR when doing a 64 bit link.  This
> @@ -2030,7 +2143,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
>    { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
>    { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
>    { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
> -  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
> +  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
> +  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
> +  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
> +  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
> +  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
>  };
>
>  static const struct elf_reloc_map mips16_reloc_map[] =
> diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
> index 489a461bb0b..8ebaa859475 100644
> --- a/bfd/elf64-mips.c
> +++ b/bfd/elf64-mips.c
> @@ -889,6 +889,118 @@ static reloc_howto_type mips_elf64_howto_table_rel[] =
>          0x0000ffff,            /* dst_mask */
>          true),                 /* pcrel_offset */
>
> +  HOWTO (R_MIPS_GOTPC_HI16,    /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_HI16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_LO16,    /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_LO16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_HI16,       /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_HI16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_LO16,       /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_LO16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_AHI16,   /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_AHI16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_ALO16,   /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_ALO16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_AHI16,      /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_AHI16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_ALO16,      /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_ALO16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
>  };
>
>  /* The relocation table used for SHT_RELA sections.  */
> @@ -1670,6 +1782,118 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
>          0x0000ffff,            /* dst_mask */
>          true),                 /* pcrel_offset */
>
> +  HOWTO (R_MIPS_GOTPC_HI16,    /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_HI16",   /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_LO16,    /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_LO16",   /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_HI16,       /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_HI16",      /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_LO16,       /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_LO16",      /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_AHI16,   /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_AHI16",  /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_ALO16,   /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_ALO16",  /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_AHI16,      /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_AHI16",     /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_ALO16,      /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_ALO16",     /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
>  };
>
>  static reloc_howto_type mips16_elf64_howto_table_rel[] =
> @@ -3743,7 +3967,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
>    { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
>    { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
>    { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
> -  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
> +  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
> +  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
> +  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
> +  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
> +  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
>  };
>
>  static const struct elf_reloc_map mips16_reloc_map[] =
> diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c
> index 7e672200006..f6a6560b4c2 100644
> --- a/bfd/elfn32-mips.c
> +++ b/bfd/elfn32-mips.c
> @@ -868,6 +868,118 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
>          0x0000ffff,            /* dst_mask */
>          true),                 /* pcrel_offset */
>
> +  HOWTO (R_MIPS_GOTPC_HI16,    /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_HI16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_LO16,    /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_LO16",   /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_HI16,       /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_HI16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_LO16,       /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_LO16",      /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_AHI16,   /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_AHI16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_ALO16,   /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_ALO16",  /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_AHI16,      /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_AHI16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_ALO16,      /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_ALO16",     /* name */
> +        true,                  /* partial_inplace */
> +        0x0000ffff,            /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
>  };
>
>  /* The relocation table used for SHT_RELA sections.  */
> @@ -1650,6 +1762,118 @@ static reloc_howto_type elf_mips_howto_table_rela[] =
>          0x0000ffff,            /* dst_mask */
>          true),                 /* pcrel_offset */
>
> +  HOWTO (R_MIPS_GOTPC_HI16,    /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_HI16",   /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_LO16,    /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_LO16",   /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_HI16,       /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_HI16",      /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_LO16,       /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_LO16",      /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_AHI16,   /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_AHI16",  /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_ALO16,   /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_ALO16",  /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_AHI16,      /* type */
> +        16,                    /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_signed, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_AHI16",     /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
> +  HOWTO (R_MIPS_GOTPC_CALL_ALO16,      /* type */
> +        0,                     /* rightshift */
> +        4,                     /* size */
> +        16,                    /* bitsize */
> +        true,                  /* pc_relative */
> +        0,                     /* bitpos */
> +        complain_overflow_dont, /* complain_on_overflow */
> +        _bfd_mips_elf_generic_reloc,   /* special_function */
> +        "R_MIPS_GOTPC_CALL_ALO16",     /* name */
> +        false,                 /* partial_inplace */
> +        0,                     /* src_mask */
> +        0x0000ffff,            /* dst_mask */
> +        true),                 /* pcrel_offset */
> +
>  };
>
>  static reloc_howto_type elf_mips16_howto_table_rel[] =
> @@ -3577,7 +3801,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
>    { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
>    { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
>    { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
> -  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
> +  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
> +  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
> +  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
> +  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
> +  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
> +  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
> +  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
>  };
>
>  static const struct elf_reloc_map mips16_reloc_map[] =
> diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
> index 89dd34e798b..c4d6787357e 100644
> --- a/bfd/elfxx-mips.c
> +++ b/bfd/elfxx-mips.c
> @@ -2267,19 +2267,28 @@ got_page_reloc_p (unsigned int r_type)
>  static inline bool
>  got_lo16_reloc_p (unsigned int r_type)
>  {
> -  return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16;
> +  return (r_type == R_MIPS_GOT_LO16
> +         || r_type == R_MIPS_GOTPC_LO16
> +         || r_type == R_MIPS_GOTPC_ALO16
> +         || r_type == R_MICROMIPS_GOT_LO16);
>  }
>
>  static inline bool
>  call_hi16_reloc_p (unsigned int r_type)
>  {
> -  return r_type == R_MIPS_CALL_HI16 || r_type == R_MICROMIPS_CALL_HI16;
> +  return (r_type == R_MIPS_CALL_HI16
> +         || r_type == R_MIPS_GOTPC_CALL_HI16
> +         || r_type == R_MIPS_GOTPC_CALL_AHI16
> +         || r_type == R_MICROMIPS_CALL_HI16);
>  }
>
>  static inline bool
>  call_lo16_reloc_p (unsigned int r_type)
>  {
> -  return r_type == R_MIPS_CALL_LO16 || r_type == R_MICROMIPS_CALL_LO16;
> +  return (r_type == R_MIPS_CALL_LO16
> +         || r_type == R_MIPS_GOTPC_CALL_LO16
> +         || r_type == R_MIPS_GOTPC_CALL_ALO16
> +         || r_type == R_MICROMIPS_CALL_LO16);
>  }
>
>  static inline bool
> @@ -5240,6 +5249,21 @@ mips_elf_highest (bfd_vma value ATTRIBUTE_UNUSED)
>    return MINUS_ONE;
>  #endif
>  }
> +
> +/**/
> +static bfd_vma
> +mips_elf_16bit_align (bfd_vma value, bfd_vma p, bool hi)
> +{
> +  bfd_vma value_lo = (value & 0xffff) + (p & 0xffff);
> +  bfd_vma value_hi = (value >> 16) & 0xffff;
> +  value_hi += mips_elf_high (value_lo);
> +  value_lo &= 0xffff;
> +  if (hi)
> +    return value_hi;
> +  else
> +    return value_lo;
> +}
> +
>
>  /* Create the .compact_rel section.  */
>
> @@ -5887,6 +5911,10 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
>      case R_MIPS_GOT_DISP:
>      case R_MIPS_GOT_LO16:
>      case R_MIPS_CALL_LO16:
> +    case R_MIPS_GOTPC_LO16:
> +    case R_MIPS_GOTPC_CALL_LO16:
> +    case R_MIPS_GOTPC_ALO16:
> +    case R_MIPS_GOTPC_CALL_ALO16:
>      case R_MICROMIPS_CALL16:
>      case R_MICROMIPS_GOT16:
>      case R_MICROMIPS_GOT_PAGE:
> @@ -5904,6 +5932,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
>        /* Fall through.  */
>      case R_MIPS_GOT_HI16:
>      case R_MIPS_CALL_HI16:
> +    case R_MIPS_GOTPC_HI16:
> +    case R_MIPS_GOTPC_CALL_HI16:
>      case R_MICROMIPS_GOT_HI16:
>      case R_MICROMIPS_CALL_HI16:
>        if (resolved_to_zero
> @@ -5951,6 +5981,14 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
>      case R_MIPS_CALL_HI16:
>      case R_MIPS_GOT_LO16:
>      case R_MIPS_CALL_LO16:
> +    case R_MIPS_GOTPC_HI16:
> +    case R_MIPS_GOTPC_LO16:
> +    case R_MIPS_GOTPC_CALL_HI16:
> +    case R_MIPS_GOTPC_CALL_LO16:
> +    case R_MIPS_GOTPC_AHI16:
> +    case R_MIPS_GOTPC_ALO16:
> +    case R_MIPS_GOTPC_CALL_AHI16:
> +    case R_MIPS_GOTPC_CALL_ALO16:
>      case R_MICROMIPS_CALL16:
>      case R_MICROMIPS_GOT16:
>      case R_MICROMIPS_GOT_DISP:
> @@ -6412,6 +6450,45 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
>        value &= howto->dst_mask;
>        break;
>
> +    /* For r6 and pre-r6, we can use:
> +       bal     . + 8
> +       lui     $2,%gotpc_hi(sym) # or %gotpc_call_hi
> +       addu    $2,$2,$ra
> +       lw      $2,%gotpc_lo(sym) # or %gotpc_call_lo
> +       In this case, the LO should +=4, and HI should -=4.
> +
> +       For R6+, we can use
> +       aluipc  $2,%gotpc_ahi(sym) # or %gotpc_call_ahi
> +       lw      $2,%gotpc_alo(sym)($2) or %gotpc_call_alo
> +       In this case, the HI is OK, while LO should +4 and add the page_offet */
> +    case R_MIPS_GOTPC_HI16:
> +    case R_MIPS_GOTPC_CALL_HI16:
> +      value = mips_elf_high (addend + gp - p + g - 4);
> +      value &= howto->dst_mask;
> +      break;
> +
> +    case R_MIPS_GOTPC_LO16:
> +    case R_MIPS_GOTPC_CALL_LO16:
> +      if (howto->partial_inplace)
> +       addend = _bfd_mips_elf_sign_extend (addend, 16);
> +      value = addend + gp - p + g + 4;
> +      value &= howto->dst_mask;
> +      break;
> +
> +    case R_MIPS_GOTPC_AHI16:
> +    case R_MIPS_GOTPC_CALL_AHI16:
> +      value = mips_elf_16bit_align (addend + gp - p + g, p, true);
> +      value &= howto->dst_mask;
> +      break;
> +
> +    case R_MIPS_GOTPC_ALO16:
> +    case R_MIPS_GOTPC_CALL_ALO16:
> +      if (howto->partial_inplace)
> +       addend = _bfd_mips_elf_sign_extend (addend, 16);
> +      value = mips_elf_16bit_align (addend + gp - p + g, p, false);
> +      value &= howto->dst_mask;
> +      break;
> +
>      case R_MIPS_PCHI16:
>        value = mips_elf_high (symbol + addend - p);
>        value &= howto->dst_mask;
> @@ -8282,6 +8359,14 @@ mips_elf_add_lo16_rel_addend (bfd *abfd,
>      lo16_type = R_MIPS16_LO16;
>    else if (micromips_reloc_p (r_type))
>      lo16_type = R_MICROMIPS_LO16;
> +  else if (r_type == R_MIPS_GOTPC_HI16)
> +    lo16_type = R_MIPS_GOTPC_LO16;
> +  else if (r_type == R_MIPS_GOTPC_CALL_HI16)
> +    lo16_type = R_MIPS_GOTPC_CALL_LO16;
> +  else if (r_type == R_MIPS_GOTPC_AHI16)
> +    lo16_type = R_MIPS_GOTPC_ALO16;
> +  else if (r_type == R_MIPS_GOTPC_CALL_AHI16)
> +    lo16_type = R_MIPS_GOTPC_CALL_ALO16;
>    else if (r_type == R_MIPS_PCHI16)
>      lo16_type = R_MIPS_PCLO16;
>    else
> @@ -8742,6 +8827,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>         case R_MIPS_CALL16:
>         case R_MIPS_CALL_HI16:
>         case R_MIPS_CALL_LO16:
> +       case R_MIPS_GOTPC_CALL_HI16:
> +       case R_MIPS_GOTPC_CALL_LO16:
> +       case R_MIPS_GOTPC_CALL_AHI16:
> +       case R_MIPS_GOTPC_CALL_ALO16:
>         case R_MIPS16_CALL16:
>         case R_MICROMIPS_CALL16:
>         case R_MICROMIPS_CALL_HI16:
> @@ -8751,6 +8840,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>
>         case R_MIPS_GOT16:
>         case R_MIPS_GOT_LO16:
> +       case R_MIPS_GOTPC_LO16:
> +       case R_MIPS_GOTPC_ALO16:
>         case R_MIPS_GOT_PAGE:
>         case R_MIPS_GOT_DISP:
>         case R_MIPS16_GOT16:
> @@ -8788,6 +8879,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>
>           /* Fall through.  */
>         case R_MIPS_GOT_HI16:
> +       case R_MIPS_GOTPC_HI16:
> +       case R_MIPS_GOTPC_AHI16:
>         case R_MIPS_GOT_OFST:
>         case R_MIPS_TLS_GOTTPREL:
>         case R_MIPS_TLS_GD:
> @@ -8958,6 +9051,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>
>         case R_MIPS_CALL_HI16:
>         case R_MIPS_CALL_LO16:
> +       case R_MIPS_GOTPC_CALL_HI16:
> +       case R_MIPS_GOTPC_CALL_LO16:
> +       case R_MIPS_GOTPC_CALL_AHI16:
> +       case R_MIPS_GOTPC_CALL_ALO16:
>         case R_MICROMIPS_CALL_HI16:
>         case R_MICROMIPS_CALL_LO16:
>           if (h != NULL)
> @@ -8983,6 +9080,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
>         case R_MIPS_GOT16:
>         case R_MIPS_GOT_HI16:
>         case R_MIPS_GOT_LO16:
> +       case R_MIPS_GOTPC_HI16:
> +       case R_MIPS_GOTPC_LO16:
> +       case R_MIPS_GOTPC_AHI16:
> +       case R_MIPS_GOTPC_ALO16:
>         case R_MICROMIPS_GOT16:
>         case R_MICROMIPS_GOT_HI16:
>         case R_MICROMIPS_GOT_LO16:
> diff --git a/bfd/libbfd.h b/bfd/libbfd.h
> index f15b5f27db8..c10ee485abf 100644
> --- a/bfd/libbfd.h
> +++ b/bfd/libbfd.h
> @@ -1273,6 +1273,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
>    "BFD_RELOC_MIPS_26_PCREL_S2",
>    "BFD_RELOC_MIPS_18_PCREL_S3",
>    "BFD_RELOC_MIPS_19_PCREL_S2",
> +  "BFD_RELOC_MIPS_LO16_GOTOFF_CALL",
> +  "BFD_RELOC_MIPS_HI16_GOTOFF_CALL",
> +  "BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL",
> +  "BFD_RELOC_MIPS_ALO16_GOTOFF",
> +  "BFD_RELOC_MIPS_AHI16_GOTOFF",
> +  "BFD_RELOC_MIPS_AHI16_S_GOTOFF",
> +  "BFD_RELOC_MIPS_ALO16_GOTOFF_CALL",
> +  "BFD_RELOC_MIPS_AHI16_GOTOFF_CALL",
> +  "BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL",
>    "BFD_RELOC_MICROMIPS_GPREL16",
>    "BFD_RELOC_MICROMIPS_HI16",
>    "BFD_RELOC_MICROMIPS_HI16_S",
> diff --git a/bfd/reloc.c b/bfd/reloc.c
> index a187afe9b56..b88763c850b 100644
> --- a/bfd/reloc.c
> +++ b/bfd/reloc.c
> @@ -2040,6 +2040,24 @@ ENUMX
>    BFD_RELOC_MIPS_18_PCREL_S3
>  ENUMX
>    BFD_RELOC_MIPS_19_PCREL_S2
> +ENUMX
> +  BFD_RELOC_LO16_GOTOFF_CALL
> +ENUMX
> +  BFD_RELOC_HI16_GOTOFF_CALL
> +ENUMX
> +  BFD_RELOC_HI16_S_GOTOFF_CALL
> +ENUMX
> +  BFD_RELOC_ALO16_GOTOFF
> +ENUMX
> +  BFD_RELOC_AHI16_GOTOFF
> +ENUMX
> +  BFD_RELOC_AHI16_S_GOTOFF
> +ENUMX
> +  BFD_RELOC_ALO16_GOTOFF_CALL
> +ENUMX
> +  BFD_RELOC_AHI16_GOTOFF_CALL
> +ENUMX
> +  BFD_RELOC_AHI16_S_GOTOFF_CALL
>  ENUMDOC
>    MIPS PC-relative relocations.
>
> diff --git a/elfcpp/mips.h b/elfcpp/mips.h
> index e8a8e2458e9..79884bd651b 100644
> --- a/elfcpp/mips.h
> +++ b/elfcpp/mips.h
> @@ -104,6 +104,14 @@ enum
>    R_MIPS_PC19_S2 = 63,
>    R_MIPS_PCHI16 = 64,
>    R_MIPS_PCLO16 = 65,
> +  R_MIPS_GOTPC_HI16 = 66,
> +  R_MIPS_GOTPC_LO16 = 67,
> +  R_MIPS_GOTPC_CALL_HI16 = 68,
> +  R_MIPS_GOTPC_CALL_LO16 = 69,
> +  R_MIPS_GOTPC_AHI16 = 70,
> +  R_MIPS_GOTPC_ALO16 = 71,
> +  R_MIPS_GOTPC_CALL_AHI16 = 72,
> +  R_MIPS_GOTPC_CALL_ALO16 = 73,
>    // These relocs are used for the mips16.
>    R_MIPS16_26 = 100,
>    R_MIPS16_GPREL = 101,
> diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
> index 8f54cb8937a..7ee6c0259fa 100644
> --- a/gas/config/tc-mips.c
> +++ b/gas/config/tc-mips.c
> @@ -4372,6 +4372,14 @@ limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
>      case BFD_RELOC_32_PCREL:
>      case BFD_RELOC_HI16_S_PCREL:
>      case BFD_RELOC_LO16_PCREL:
> +    case BFD_RELOC_HI16_S_GOTOFF:
> +    case BFD_RELOC_LO16_GOTOFF:
> +    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
>        return HAVE_64BIT_ADDRESSES;
>
>      default:
> @@ -7432,6 +7440,8 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
>
>      case BFD_RELOC_HI16_S:
>      case BFD_RELOC_HI16_S_PCREL:
> +    case BFD_RELOC_HI16_S_GOTOFF:
> +    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
>      case BFD_RELOC_MICROMIPS_HI16_S:
>      case BFD_RELOC_MIPS16_HI16_S:
>        *result = ((operand + 0x8000) >> 16) & 0xffff;
> @@ -7445,6 +7455,10 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
>
>      case BFD_RELOC_LO16:
>      case BFD_RELOC_LO16_PCREL:
> +    case BFD_RELOC_LO16_GOTOFF:
> +    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
>      case BFD_RELOC_MICROMIPS_LO16:
>      case BFD_RELOC_MIPS16_LO16:
>        *result = operand & 0xffff;
> @@ -7507,6 +7521,22 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
>      as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
>              (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
>
> +  if (!ISA_IS_R6 (mips_opts.isa))
> +    switch (*reloc_type)
> +      {
> +       default:
> +         break;
> +
> +       case BFD_RELOC_MIPS_ALO16_GOTOFF:
> +       case BFD_RELOC_MIPS_AHI16_GOTOFF:
> +       case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
> +       case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
> +       case BFD_RELOC_MIPS_AHI16_GOTOFF_CALL:
> +       case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
> +         as_fatal (_("ALUIPC style GOTPC cannot work with pre-R6."));
> +         break;
> +      }
> +
>    if (address_expr == NULL)
>      ip->complete_p = 1;
>    else if (reloc_type[0] <= BFD_RELOC_UNUSED
> @@ -14583,7 +14613,15 @@ static const struct percent_op_match mips_percent_op[] =
>    {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
>    {"%hi", BFD_RELOC_HI16_S},
>    {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
> -  {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
> +  {"%pcrel_lo", BFD_RELOC_LO16_PCREL},
> +  {"%gotpc_hi", BFD_RELOC_HI16_S_GOTOFF},
> +  {"%gotpc_lo", BFD_RELOC_LO16_GOTOFF},
> +  {"%gotpc_call_hi", BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL},
> +  {"%gotpc_call_lo", BFD_RELOC_MIPS_LO16_GOTOFF_CALL},
> +  {"%gotpc_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF},
> +  {"%gotpc_alo", BFD_RELOC_MIPS_ALO16_GOTOFF},
> +  {"%gotpc_call_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL},
> +  {"%gotpc_call_alo", BFD_RELOC_MIPS_ALO16_GOTOFF_CALL}
>  };
>
>  static const struct percent_op_match mips16_percent_op[] =
> @@ -15543,6 +15581,14 @@ mips_force_relocation (fixS *fixp)
>           || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
>           || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
>           || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
> +         || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
> +         || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
> +         || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
>           || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
>           || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
>      return 1;
> @@ -15824,6 +15870,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
>        case BFD_RELOC_MIPS_19_PCREL_S2:
>        case BFD_RELOC_HI16_S_PCREL:
>        case BFD_RELOC_LO16_PCREL:
> +      case BFD_RELOC_HI16_S_GOTOFF:
> +      case BFD_RELOC_LO16_GOTOFF:
> +      case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
> +      case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
> +      case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
> +      case BFD_RELOC_MIPS_ALO16_GOTOFF:
> +      case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
> +      case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
>         break;
>
>        case BFD_RELOC_32:
> @@ -15967,6 +16021,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
>      case BFD_RELOC_MIPS_GOT_LO16:
>      case BFD_RELOC_MIPS_CALL_HI16:
>      case BFD_RELOC_MIPS_CALL_LO16:
> +    case BFD_RELOC_HI16_S_GOTOFF:
> +    case BFD_RELOC_LO16_GOTOFF:
> +    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF:
> +    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
> +    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
>      case BFD_RELOC_HI16_S_PCREL:
>      case BFD_RELOC_LO16_PCREL:
>      case BFD_RELOC_MIPS16_GPREL:
> @@ -18388,6 +18450,14 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
>                   || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
>                   || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
>                   || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
> +                 || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
> +                 || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
> +                 || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
>                   || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
>                   || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
>
> diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-32.s b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
> new file mode 100644
> index 00000000000..877193313a1
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
> @@ -0,0 +1,51 @@
> +       .file   1 "nn.c"
> +       .section .mdebug.abi32
> +       .previous
> +       .nan    2008
> +       .module fp=xx
> +       .module nooddspreg
> +       .module arch=mips32r6
> +       .abicalls
> +       .text
> +       .section        .rodata.str1.4,"aMS",@progbits,1
> +       .align  2
> +$LC0:
> +       .ascii  "XXXX\000"
> +       .section        .text.startup,"ax",@progbits
> +       .align  2
> +       .globl  main
> +       .set    nomips16
> +       .ent    main
> +       .type   main, @function
> +main:
> +       .frame  $sp,32,$31              # vars= 0, regs= 1/0, args= 16, gp= 8
> +       .mask   0x80000000,-4
> +       .fmask  0x00000000,0
> +       .set    noreorder
> +       .cpload $25
> +       .set    nomacro
> +       addiu   $sp,$sp,-32
> +       sw      $31,28($sp)
> +
> +       aluipc  $4,%gotpc_ahi($LC0)
> +       lw      $4,%gotpc_alo($LC0)($4)
> +
> +       aluipc  $25,%gotpc_call_ahi(puts)
> +       lw      $25,%gotpc_call_alo(puts)($25)
> +
> +       .cprestore      16
> +       .reloc  1f,R_MIPS_JALR,puts
> +1:     jalr    $25
> +       nop
> +
> +       lw      $31,28($sp)
> +       move    $2,$0
> +       jr      $31
> +       addiu   $sp,$sp,32
> +
> +       .set    macro
> +       .set    reorder
> +       .end    main
> +       .size   main, .-main
> +       .ident  "GCC: (Debian 12.2.0-14) 12.2.0"
> +       .section        .note.GNU-stack,"",@progbits
> diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-64.s b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
> new file mode 100644
> index 00000000000..234be37b6fe
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
> @@ -0,0 +1,50 @@
> +       .file   1 "nn.c"
> +       .section .mdebug.abi64
> +       .previous
> +       .abicalls
> +       .text
> +       .section        .rodata.str1.8,"aMS",@progbits,1
> +       .align  3
> +.LC0:
> +       .ascii  "XXXX\000"
> +       .section        .text.startup,"ax",@progbits
> +       .align  2
> +       .align  3
> +       .globl  main
> +       .set    nomips16
> +       .ent    main
> +       .type   main, @function
> +main:
> +       .frame  $sp,16,$31              # vars= 0, regs= 2/0, args= 0, gp= 0
> +       .mask   0x90000000,-8
> +       .fmask  0x00000000,0
> +       .set    noreorder
> +       .set    nomacro
> +       daddiu  $sp,$sp,-16
> +       sd      $28,0($sp)
> +       lui     $28,%hi(%neg(%gp_rel(main)))
> +       daddu   $28,$28,$25
> +       sd      $31,8($sp)
> +       daddiu  $28,$28,%lo(%neg(%gp_rel(main)))
> +
> +       aluipc  $4,%gotpc_ahi(.LC0)
> +       ld      $4,%gotpc_alo(.LC0)($4)
> +
> +       aluipc  $25,%gotpc_call_ahi(puts)
> +       ld      $25,%gotpc_call_alo(puts)($25)
> +
> +       .reloc  1f,R_MIPS_JALR,puts
> +1:     jalr    $25
> +       nop
> +
> +       ld      $31,8($sp)
> +       ld      $28,0($sp)
> +       move    $2,$0
> +       jr      $31
> +       daddiu  $sp,$sp,16
> +
> +       .set    macro
> +       .set    reorder
> +       .end    main
> +       .size   main, .-main
> +       .section        .note.GNU-stack,"",@progbits
> diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n32.d b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
> new file mode 100644
> index 00000000000..41bc6d474d7
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
> @@ -0,0 +1,17 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N32)
> +#source: gotpc-aluipc-64.s
> +#as: -n32 -mips64r6
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_AHI16   \.rodata\.str1\.8
> +#...
> +.*R_MIPS_GOTPC_ALO16   \.rodata\.str1\.8
> +#...
> +.*R_MIPS_GOTPC_CALL_AHI16      puts
> +#...
> +.*R_MIPS_GOTPC_CALL_ALO16      puts
> +#pass
> diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n64.d b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
> new file mode 100644
> index 00000000000..0561040ef09
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
> @@ -0,0 +1,25 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N64)
> +#source: gotpc-aluipc-64.s
> +#as: -64 -mips64r6
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_AHI16   \.LC0
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_ALO16   \.LC0
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_CALL_AHI16      puts
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_CALL_ALO16      puts
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#pass
> diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-o32.d b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
> new file mode 100644
> index 00000000000..1832061a34e
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
> @@ -0,0 +1,17 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (OldABI, ALUIPC, O32)
> +#source: gotpc-aluipc-32.s
> +#as: -32 -mips32r6
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_AHI16   \.rodata\.str1\.4
> +#...
> +.*R_MIPS_GOTPC_ALO16   \.rodata\.str1\.4
> +#...
> +.*R_MIPS_GOTPC_CALL_AHI16      puts
> +#...
> +.*R_MIPS_GOTPC_CALL_ALO16      puts
> +#pass
> diff --git a/gas/testsuite/gas/mips/gotpc-bal-32.s b/gas/testsuite/gas/mips/gotpc-bal-32.s
> new file mode 100644
> index 00000000000..841a13dbf87
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-bal-32.s
> @@ -0,0 +1,55 @@
> +       .file   1 "nn.c"
> +       .section .mdebug.abi32
> +       .previous
> +       .nan    2008
> +       .module fp=xx
> +       .module nooddspreg
> +       .module arch=mips32r6
> +       .abicalls
> +       .text
> +       .section        .rodata.str1.4,"aMS",@progbits,1
> +       .align  2
> +$LC0:
> +       .ascii  "XXXX\000"
> +       .section        .text.startup,"ax",@progbits
> +       .align  2
> +       .globl  main
> +       .set    nomips16
> +       .ent    main
> +       .type   main, @function
> +main:
> +       .frame  $sp,32,$31              # vars= 0, regs= 1/0, args= 16, gp= 8
> +       .mask   0x80000000,-4
> +       .fmask  0x00000000,0
> +       .set    noreorder
> +       .cpload $25
> +       .set    nomacro
> +       addiu   $sp,$sp,-32
> +       sw      $31,28($sp)
> +
> +       bal     . + 8
> +       lui     $4,%gotpc_hi($LC0)
> +       addu    $4,$4,$31
> +       lw      $4,%gotpc_lo($LC0)($4)
> +
> +       bal     . + 8
> +       lui     $25,%gotpc_call_hi(puts)
> +       addu    $25,$25,$31
> +       lw      $25,%gotpc_call_lo(puts)($25)
> +
> +       .cprestore      16
> +       .reloc  1f,R_MIPS_JALR,puts
> +1:     jalr    $25
> +       nop
> +
> +       lw      $31,28($sp)
> +       move    $2,$0
> +       jr      $31
> +       addiu   $sp,$sp,32
> +
> +       .set    macro
> +       .set    reorder
> +       .end    main
> +       .size   main, .-main
> +       .ident  "GCC: (Debian 12.2.0-14) 12.2.0"
> +       .section        .note.GNU-stack,"",@progbits
> diff --git a/gas/testsuite/gas/mips/gotpc-bal-64.s b/gas/testsuite/gas/mips/gotpc-bal-64.s
> new file mode 100644
> index 00000000000..c97e6b87af9
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-bal-64.s
> @@ -0,0 +1,54 @@
> +       .file   1 "nn.c"
> +       .section .mdebug.abi64
> +       .previous
> +       .abicalls
> +       .text
> +       .section        .rodata.str1.8,"aMS",@progbits,1
> +       .align  3
> +.LC0:
> +       .ascii  "XXXX\000"
> +       .section        .text.startup,"ax",@progbits
> +       .align  2
> +       .align  3
> +       .globl  main
> +       .set    nomips16
> +       .ent    main
> +       .type   main, @function
> +main:
> +       .frame  $sp,16,$31              # vars= 0, regs= 2/0, args= 0, gp= 0
> +       .mask   0x90000000,-8
> +       .fmask  0x00000000,0
> +       .set    noreorder
> +       .set    nomacro
> +       daddiu  $sp,$sp,-16
> +       sd      $28,0($sp)
> +       lui     $28,%hi(%neg(%gp_rel(main)))
> +       daddu   $28,$28,$25
> +       sd      $31,8($sp)
> +       daddiu  $28,$28,%lo(%neg(%gp_rel(main)))
> +
> +       bal     . + 8
> +       lui     $4,%gotpc_hi(.LC0)
> +       daddu   $4,$4,$31
> +       ld      $4,%gotpc_lo(.LC0)($4)
> +
> +       bal     . + 8
> +       lui     $25,%gotpc_call_hi(puts)
> +       daddu   $25,$25,$31
> +       ld      $25,%gotpc_call_lo(puts)($25)
> +
> +       .reloc  1f,R_MIPS_JALR,puts
> +1:     jalr    $25
> +       nop
> +
> +       ld      $31,8($sp)
> +       ld      $28,0($sp)
> +       move    $2,$0
> +       jr      $31
> +       daddiu  $sp,$sp,16
> +
> +       .set    macro
> +       .set    reorder
> +       .end    main
> +       .size   main, .-main
> +       .section        .note.GNU-stack,"",@progbits
> diff --git a/gas/testsuite/gas/mips/gotpc-bal-n32.d b/gas/testsuite/gas/mips/gotpc-bal-n32.d
> new file mode 100644
> index 00000000000..ec412020dc7
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-bal-n32.d
> @@ -0,0 +1,17 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (NewABI, BAL, N32)
> +#source: gotpc-bal-64.s
> +#as: -n32 -march=from-abi
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_HI16    \.rodata\.str1\.8
> +#...
> +.*R_MIPS_GOTPC_LO16    \.rodata\.str1\.8
> +#...
> +.*R_MIPS_GOTPC_CALL_HI16       puts
> +#...
> +.*R_MIPS_GOTPC_CALL_LO16       puts
> +#pass
> diff --git a/gas/testsuite/gas/mips/gotpc-bal-n64.d b/gas/testsuite/gas/mips/gotpc-bal-n64.d
> new file mode 100644
> index 00000000000..60b9f020f50
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-bal-n64.d
> @@ -0,0 +1,25 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (NewABI, BAL, N64)
> +#source: gotpc-bal-64.s
> +#as: -64 -march=from-abi
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_HI16    \.rodata\.str1\.8
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_LO16    \.rodata\.str1\.8
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_CALL_HI16       puts
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#...
> +.*R_MIPS_GOTPC_CALL_LO16       puts
> +.*R_MIPS_NONE.*
> +.*R_MIPS_NONE.*
> +#pass
> diff --git a/gas/testsuite/gas/mips/gotpc-bal-o32.d b/gas/testsuite/gas/mips/gotpc-bal-o32.d
> new file mode 100644
> index 00000000000..57cb891042a
> --- /dev/null
> +++ b/gas/testsuite/gas/mips/gotpc-bal-o32.d
> @@ -0,0 +1,17 @@
> +#objdump: -dr --prefix-addresses --show-raw-insn
> +#name: R_MIPS_GOTPC support (OldABI, BAL, O32)
> +#source: gotpc-bal-32.s
> +#as: -32 -march=from-abi
> +
> +.*: +file format .*mips.*
> +
> +Disassembly of section \.text\.startup:
> +#...
> +.*R_MIPS_GOTPC_HI16    \.rodata\.str1\.4
> +#...
> +.*R_MIPS_GOTPC_LO16    \.rodata\.str1\.4
> +#...
> +.*R_MIPS_GOTPC_CALL_HI16       puts
> +#...
> +.*R_MIPS_GOTPC_CALL_LO16       puts
> +#pass
> diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
> index 2ddbf0c768d..619d04b3bd5 100644
> --- a/gas/testsuite/gas/mips/mips.exp
> +++ b/gas/testsuite/gas/mips/mips.exp
> @@ -1210,7 +1210,15 @@ if { [istarget mips*-*-vxworks*] } {
>         run_dump_test "elf-rel-xgot-n32"
>         run_dump_test "elf-rel-got-n64"
>         run_dump_test "elf-rel-xgot-n64"
> +
> +       run_dump_test "gotpc-bal-n64"
> +       run_dump_test "gotpc-bal-n32"
> +       run_dump_test "gotpc-aluipc-n64"
> +       run_dump_test "gotpc-aluipc-n32"
>      }
> +    run_dump_test "gotpc-bal-o32"
> +    run_dump_test "gotpc-aluipc-o32"
> +
>      run_dump_test "elf-rel17"
>      if $has_newabi {
>         run_dump_test "elf-rel18"
> diff --git a/include/elf/mips.h b/include/elf/mips.h
> index 686d5500e02..23e95fe0c45 100644
> --- a/include/elf/mips.h
> +++ b/include/elf/mips.h
> @@ -98,7 +98,15 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
>    RELOC_NUMBER (R_MIPS_PC19_S2, 63)
>    RELOC_NUMBER (R_MIPS_PCHI16, 64)
>    RELOC_NUMBER (R_MIPS_PCLO16, 65)
> -  FAKE_RELOC (R_MIPS_max, 66)
> +  RELOC_NUMBER (R_MIPS_GOTPC_HI16, 66)
> +  RELOC_NUMBER (R_MIPS_GOTPC_LO16, 67)
> +  RELOC_NUMBER (R_MIPS_GOTPC_CALL_HI16, 68)
> +  RELOC_NUMBER (R_MIPS_GOTPC_CALL_LO16, 69)
> +  RELOC_NUMBER (R_MIPS_GOTPC_AHI16, 70)
> +  RELOC_NUMBER (R_MIPS_GOTPC_ALO16, 71)
> +  RELOC_NUMBER (R_MIPS_GOTPC_CALL_AHI16, 72)
> +  RELOC_NUMBER (R_MIPS_GOTPC_CALL_ALO16, 73)
> +  FAKE_RELOC (R_MIPS_max, 74)
>    /* These relocs are used for the mips16.  */
>    FAKE_RELOC (R_MIPS16_min, 100)
>    RELOC_NUMBER (R_MIPS16_26, 100)
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-callee.s b/ld/testsuite/ld-mips-elf/gotpc-callee.s
> new file mode 100644
> index 00000000000..4689b66f302
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-callee.s
> @@ -0,0 +1,41 @@
> +       .text
> +       .set    nomips16
> +       .set    nomicromips
> +       .globl  c1
> +       .ent    c1
> +       .type   c1, @function
> +c1:
> +       .set    noreorder
> +       .set    nomacro
> +       jr      $31
> +       li      $2,1
> +       .set    reorder
> +       .set    macro
> +       .end    c1
> +       .size   c1, .-c1
> +
> +       .globl  c2
> +       .ent    c2
> +       .type   c2, @function
> +c2:
> +       .set    noreorder
> +       .set    nomacro
> +       jr      $31
> +       li      $2,2
> +       .set    reorder
> +       .set    macro
> +       .end    c2
> +       .size   c2, .-c2
> +
> +       .data
> +       .globl  g1
> +       .type   g1, @object
> +       .size   g1, 4
> +g1:
> +       .word   0x12345678
> +
> +       .globl  g2
> +       .type   g2, @object
> +       .size   g2, 4
> +g2:
> +       .word   0x09abcdef
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.dd b/ld/testsuite/ld-mips-elf/gotpc-n32.dd
> new file mode 100644
> index 00000000000..2c94d04ee27
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n32.dd
> @@ -0,0 +1,73 @@
> +
> +.*: +file format .*mips.*
> +
> +DYNAMIC SYMBOL TABLE:
> +00000000 g    DF .text 00000074 f1
> +00000090      DF \*UND\*       00000000 c1
> +00000004 g    DO \*ABS\*       00000004 g2
> +00000080      DF \*UND\*       00000000 c2
> +00000000 g    DO \*ABS\*       00000004 g1
> +
> +
> +
> +Disassembly of section .text:
> +
> +00000000 <f1>:
> +   0:  27bdfff0        addiu   sp,sp,-16
> +   4:  ffbf0008        sd      ra,8\(sp\)
> +   8:  ffb00000        sd      s0,0\(sp\)
> +   c:  24100000        li      s0,0
> +
> +00000010 <L0\^A>:
> +  10:  04110001        bal     18 <L0\^A\+0x8>
> +  14:  3c190000        lui     t9,0x0
> +  18:  033fc821        addu    t9,t9,ra
> +  1c:  8f3900a0        lw      t9,160\(t9\)
> +  20:  0320f809        jalr    t9
> +  24:  00000000        nop
> +  28:  02028021        addu    s0,s0,v0
> +  2c:  ef3f0000        aluipc  t9,0x0
> +  30:  8f3900c0        lw      t9,192\(t9\)
> +  34:  0320f809        jalr    t9
> +  38:  00000000        nop
> +  3c:  02028021        addu    s0,s0,v0
> +
> +00000040 <L0\^A>:
> +  40:  04110001        bal     48 <L0\^A\+0x8>
> +  44:  3c020000        lui     v0,0x0
> +  48:  005f1021        addu    v0,v0,ra
> +  4c:  8c42007c        lw      v0,124\(v0\)
> +  50:  00501021        addu    v0,v0,s0
> +  54:  ec7f0000        aluipc  v1,0x0
> +  58:  8c6300bc        lw      v1,188\(v1\)
> +  5c:  00431021        addu    v0,v0,v1
> +  60:  dfb00000        ld      s0,0\(sp\)
> +  64:  dfbf0008        ld      ra,8\(sp\)
> +  68:  24420001        addiu   v0,v0,1
> +  6c:  03e00009        jr      ra
> +  70:  27bd0010        addiu   sp,sp,16
> +       ...
> +
> +Disassembly of section .MIPS.stubs:
> +
> +00000080 <_MIPS_STUBS_>:
> +  80:  8f998010        lw      t9,-32752\(gp\)
> +  84:  03e07825        move    t3,ra
> +  88:  0320f809        jalr    t9
> +  8c:  24180004        li      t8,4
> +  90:  8f998010        lw      t9,-32752\(gp\)
> +  94:  03e07825        move    t3,ra
> +  98:  0320f809        jalr    t9
> +  9c:  24180002        li      t8,2
> +       ...
> +
> +Disassembly of section .got:
> +
> +000000b0 <.got>:
> +  b0:  00000000        nop
> +  b4:  80000000        lb      zero,0\(zero\)
> +  b8:  00000090        .word   0x90
> +  bc:  00000004        sllv    zero,zero,zero
> +  c0:  00000080        sll     zero,zero,0x2
> +  c4:  00000000        nop
> +#...
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.gd b/ld/testsuite/ld-mips-elf/gotpc-n32.gd
> new file mode 100644
> index 00000000000..33c9b40ab77
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n32.gd
> @@ -0,0 +1,15 @@
> +
> +Primary GOT:
> + Canonical gp value: 000080a0
> +
> + Reserved entries:
> +   Address     Access  Initial Purpose
> +  000000b0 -32752\(gp\) 00000000 Lazy resolver
> +  000000b4 -32748\(gp\) 80000000 Module pointer \(GNU extension\)
> +
> + Global entries:
> +   Address     Access  Initial Sym.Val. Type    Ndx Name
> +  000000b8 -32744\(gp\) 00000090 00000090 FUNC    UND c1
> +  000000bc -32740\(gp\) 00000004 00000004 OBJECT  ABS g2
> +  000000c0 -32736\(gp\) 00000080 00000080 FUNC    UND c2
> +  000000c4 -32732\(gp\) 00000000 00000000 OBJECT  ABS g1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.s b/ld/testsuite/ld-mips-elf/gotpc-n32.s
> new file mode 100644
> index 00000000000..ce4f4af87f1
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n32.s
> @@ -0,0 +1,46 @@
> +       .text
> +       .globl  f1
> +       .set    nomips16
> +       .set    nomicromips
> +       .ent    f1
> +       .type   f1, @function
> +f1:
> +       .set    noreorder
> +       .set    nomacro
> +       addiu   $sp,$sp,-16
> +       sd      $31,8($sp)
> +       sd      $16,0($sp)
> +       li      $16,0
> +
> +       bal     . + 8
> +       lui     $25,%gotpc_call_hi(c1)
> +       addu    $25,$25,$31
> +       lw      $25,%gotpc_call_lo(c1)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +       aluipc  $25,%gotpc_call_ahi(c2)
> +       lw      $25,%gotpc_call_alo(c2)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +
> +       bal     . + 8
> +       lui     $2,%gotpc_hi(g1)
> +       addu    $2,$2,$31
> +       lw      $2,%gotpc_lo(g1)($2)
> +       addu    $2,$2,$16
> +       aluipc  $3,%gotpc_ahi(g2)
> +       lw      $3,%gotpc_alo(g2)($3)
> +       addu    $2,$2,$3
> +
> +       ld      $16,0($sp)
> +       ld      $31,8($sp)
> +       addiu   $2,$2,1
> +       jr      $31
> +       addiu   $sp,$sp,16
> +
> +       .set    macro
> +       .set    reorder
> +       .end    f1
> +       .size   f1, .-f1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.dd b/ld/testsuite/ld-mips-elf/gotpc-n64.dd
> new file mode 100644
> index 00000000000..08e8e2b48a1
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n64.dd
> @@ -0,0 +1,75 @@
> +
> +.*: +file format .*mips.*
> +
> +DYNAMIC SYMBOL TABLE:
> +0000000000000000 g    DF .text 0000000000000074 f1
> +0000000000000090      DF \*UND\*       0000000000000000 c1
> +0000000000000004 g    DO \*ABS\*       0000000000000004 g2
> +0000000000000080      DF \*UND\*       0000000000000000 c2
> +0000000000000000 g    DO \*ABS\*       0000000000000004 g1
> +
> +
> +
> +Disassembly of section .text:
> +
> +0000000000000000 <f1>:
> +   0:  67bdfff0        daddiu  sp,sp,-16
> +   4:  ffbf0008        sd      ra,8\(sp\)
> +   8:  ffb00000        sd      s0,0\(sp\)
> +   c:  24100000        li      s0,0
> +
> +0000000000000010 <L0\^A>:
> +  10:  04110001        bal     18 <L0\^A\+0x8>
> +  14:  3c190000        lui     t9,0x0
> +  18:  033fc82d        daddu   t9,t9,ra
> +  1c:  df3900a8        ld      t9,168\(t9\)
> +  20:  0320f809        jalr    t9
> +  24:  00000000        nop
> +  28:  02028021        addu    s0,s0,v0
> +  2c:  ef3f0000        aluipc  t9,0x0
> +  30:  df3900d0        ld      t9,208\(t9\)
> +  34:  0320f809        jalr    t9
> +  38:  00000000        nop
> +  3c:  02028021        addu    s0,s0,v0
> +
> +0000000000000040 <L0\^A>:
> +  40:  04110001        bal     48 <L0\^A\+0x8>
> +  44:  3c020000        lui     v0,0x0
> +  48:  005f102d        daddu   v0,v0,ra
> +  4c:  8c420090        lw      v0,144\(v0\)
> +  50:  00501021        addu    v0,v0,s0
> +  54:  ec7f0000        aluipc  v1,0x0
> +  58:  8c6300c8        lw      v1,200\(v1\)
> +  5c:  00431021        addu    v0,v0,v1
> +  60:  dfb00000        ld      s0,0\(sp\)
> +  64:  dfbf0008        ld      ra,8\(sp\)
> +  68:  24420001        addiu   v0,v0,1
> +  6c:  03e00009        jr      ra
> +  70:  67bd0010        daddiu  sp,sp,16
> +       ...
> +
> +Disassembly of section .MIPS.stubs:
> +
> +0000000000000080 <_MIPS_STUBS_>:
> +  80:  df998010        ld      t9,-32752\(gp\)
> +  84:  03e07825        move    t3,ra
> +  88:  0320f809        jalr    t9
> +  8c:  64180004        daddiu  t8,zero,4
> +  90:  df998010        ld      t9,-32752\(gp\)
> +  94:  03e07825        move    t3,ra
> +  98:  0320f809        jalr    t9
> +  9c:  64180002        daddiu  t8,zero,2
> +       ...
> +
> +Disassembly of section .got:
> +
> +00000000000000b0 <.got>:
> +       ...
> +  bc:  80000000        lb      zero,0\(zero\)
> +  c0:  00000090        .word   0x90
> +  c4:  00000000        nop
> +  c8:  00000004        sllv    zero,zero,zero
> +  cc:  00000000        nop
> +  d0:  00000080        sll     zero,zero,0x2
> +       ...
> +#...
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.gd b/ld/testsuite/ld-mips-elf/gotpc-n64.gd
> new file mode 100644
> index 00000000000..c08cca49ba4
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n64.gd
> @@ -0,0 +1,15 @@
> +
> +Primary GOT:
> + Canonical gp value: 00000000000080a0
> +
> + Reserved entries:
> +           Address     Access          Initial Purpose
> +  00000000000000b0 -32752\(gp\) 0000000000000000 Lazy resolver
> +  00000000000000b8 -32744\(gp\) 8000000000000000 Module pointer \(GNU extension\)
> +
> + Global entries:
> +           Address     Access          Initial         Sym.Val. Type    Ndx Name
> +  00000000000000c0 -32736\(gp\) 0000000000000090 0000000000000090 FUNC    UND c1
> +  00000000000000c8 -32728\(gp\) 0000000000000004 0000000000000004 OBJECT  ABS g2
> +  00000000000000d0 -32720\(gp\) 0000000000000080 0000000000000080 FUNC    UND c2
> +  00000000000000d8 -32712\(gp\) 0000000000000000 0000000000000000 OBJECT  ABS g1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.s b/ld/testsuite/ld-mips-elf/gotpc-n64.s
> new file mode 100644
> index 00000000000..47dbd40af2b
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-n64.s
> @@ -0,0 +1,46 @@
> +       .text
> +       .globl  f1
> +       .set    nomips16
> +       .set    nomicromips
> +       .ent    f1
> +       .type   f1, @function
> +f1:
> +       .set    noreorder
> +       .set    nomacro
> +       daddiu  $sp,$sp,-16
> +       sd      $31,8($sp)
> +       sd      $16,0($sp)
> +       li      $16,0
> +
> +       bal     . + 8
> +       lui     $25,%gotpc_call_hi(c1)
> +       daddu   $25,$25,$31
> +       ld      $25,%gotpc_call_lo(c1)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +       aluipc  $25,%gotpc_call_ahi(c2)
> +       ld      $25,%gotpc_call_alo(c2)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +
> +       bal     . + 8
> +       lui     $2,%gotpc_hi(g1)
> +       daddu   $2,$2,$31
> +       lw      $2,%gotpc_lo(g1)($2)
> +       addu    $2,$2,$16
> +       aluipc  $3,%gotpc_ahi(g2)
> +       lw      $3,%gotpc_alo(g2)($3)
> +       addu    $2,$2,$3
> +
> +       ld      $16,0($sp)
> +       ld      $31,8($sp)
> +       addiu   $2,$2,1
> +       jr      $31
> +       daddiu  $sp,$sp,16
> +
> +       .set    macro
> +       .set    reorder
> +       .end    f1
> +       .size   f1, .-f1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.dd b/ld/testsuite/ld-mips-elf/gotpc-o32.dd
> new file mode 100644
> index 00000000000..1c77b059ef6
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-o32.dd
> @@ -0,0 +1,71 @@
> +
> +.*: +file format .*mips.*
> +
> +DYNAMIC SYMBOL TABLE:
> +00000000 g    DF .text 00000070 f1
> +00000080      DF \*UND\*       00000000 c1
> +00000004 g    DO \*ABS\*       00000004 g2
> +00000070      DF \*UND\*       00000000 c2
> +00000000 g    DO \*ABS\*       00000004 g1
> +
> +
> +
> +Disassembly of section .text:
> +
> +00000000 <f1>:
> +   0:  27bdffe0        addiu   sp,sp,-32
> +   4:  afbf001c        sw      ra,28\(sp\)
> +   8:  afb00018        sw      s0,24\(sp\)
> +   c:  24100000        li      s0,0
> +
> +00000010 <L0\^A>:
> +  10:  04110001        bal     18 <L0\^A\+0x8>
> +  14:  3c190000        lui     t9,0x0
> +  18:  033fc821        addu    t9,t9,ra
> +  1c:  8f390090        lw      t9,144\(t9\)
> +  20:  0320f809        jalr    t9
> +  24:  00000000        nop
> +  28:  02028021        addu    s0,s0,v0
> +  2c:  ef3f0000        aluipc  t9,0x0
> +  30:  8f3900b0        lw      t9,176\(t9\)
> +  34:  0320f809        jalr    t9
> +  38:  00000000        nop
> +  3c:  02028021        addu    s0,s0,v0
> +
> +00000040 <L0\^A>:
> +  40:  04110001        bal     48 <L0\^A\+0x8>
> +  44:  3c020000        lui     v0,0x0
> +  48:  005f1021        addu    v0,v0,ra
> +  4c:  8c42006c        lw      v0,108\(v0\)
> +  50:  00501021        addu    v0,v0,s0
> +  54:  ec7f0000        aluipc  v1,0x0
> +  58:  8c6300ac        lw      v1,172\(v1\)
> +  5c:  00431021        addu    v0,v0,v1
> +  60:  8fb00018        lw      s0,24\(sp\)
> +  64:  8fbf001c        lw      ra,28\(sp\)
> +  68:  03e00009        jr      ra
> +  6c:  27bd0020        addiu   sp,sp,32
> +
> +Disassembly of section .MIPS.stubs:
> +
> +00000070 <_MIPS_STUBS_>:
> +  70:  8f998010        lw      t9,-32752\(gp\)
> +  74:  03e07825        move    t7,ra
> +  78:  0320f809        jalr    t9
> +  7c:  24180004        li      t8,4
> +  80:  8f998010        lw      t9,-32752\(gp\)
> +  84:  03e07825        move    t7,ra
> +  88:  0320f809        jalr    t9
> +  8c:  24180002        li      t8,2
> +       ...
> +
> +Disassembly of section .got:
> +
> +000000a0 <.got>:
> +  a0:  00000000        nop
> +  a4:  80000000        lb      zero,0\(zero\)
> +  a8:  00000080        sll     zero,zero,0x2
> +  ac:  00000004        sllv    zero,zero,zero
> +  b0:  00000070        tge     zero,zero,0x1
> +  b4:  00000000        nop
> +#...
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.gd b/ld/testsuite/ld-mips-elf/gotpc-o32.gd
> new file mode 100644
> index 00000000000..7a479237a22
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-o32.gd
> @@ -0,0 +1,15 @@
> +
> +Primary GOT:
> + Canonical gp value: 00008090
> +
> + Reserved entries:
> +   Address     Access  Initial Purpose
> +  000000a0 -32752\(gp\) 00000000 Lazy resolver
> +  000000a4 -32748\(gp\) 80000000 Module pointer \(GNU extension\)
> +
> + Global entries:
> +   Address     Access  Initial Sym.Val. Type    Ndx Name
> +  000000a8 -32744\(gp\) 00000080 00000080 FUNC    UND c1
> +  000000ac -32740\(gp\) 00000004 00000004 OBJECT  ABS g2
> +  000000b0 -32736\(gp\) 00000070 00000070 FUNC    UND c2
> +  000000b4 -32732\(gp\) 00000000 00000000 OBJECT  ABS g1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.s b/ld/testsuite/ld-mips-elf/gotpc-o32.s
> new file mode 100644
> index 00000000000..3ac3b650aa1
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc-o32.s
> @@ -0,0 +1,45 @@
> +       .text
> +       .globl  f1
> +       .set    nomips16
> +       .set    nomicromips
> +       .ent    f1
> +       .type   f1, @function
> +f1:
> +       .set    noreorder
> +       .set    nomacro
> +       addiu   $sp,$sp,-32
> +       sw      $31,28($sp)
> +       sw      $16,24($sp)
> +       li      $16,0
> +
> +       bal     . + 8
> +       lui     $25,%gotpc_call_hi(c1)
> +       addu    $25,$25,$31
> +       lw      $25,%gotpc_call_lo(c1)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +       aluipc  $25,%gotpc_call_ahi(c2)
> +       lw      $25,%gotpc_call_alo(c2)($25)
> +       jalr    $25
> +       nop
> +       addu    $16,$16,$2
> +
> +       bal     . + 8
> +       lui     $2,%gotpc_hi(g1)
> +       addu    $2,$2,$31
> +       lw      $2,%gotpc_lo(g1)($2)
> +       addu    $2,$2,$16
> +       aluipc  $3,%gotpc_ahi(g2)
> +       lw      $3,%gotpc_alo(g2)($3)
> +       addu    $2,$2,$3
> +
> +       lw      $16,24($sp)
> +       lw      $31,28($sp)
> +       jr      $31
> +       addiu   $sp,$sp,32
> +
> +       .set    macro
> +       .set    reorder
> +       .end    f1
> +       .size   f1, .-f1
> diff --git a/ld/testsuite/ld-mips-elf/gotpc.ld b/ld/testsuite/ld-mips-elf/gotpc.ld
> new file mode 100644
> index 00000000000..3dadc7e6d4d
> --- /dev/null
> +++ b/ld/testsuite/ld-mips-elf/gotpc.ld
> @@ -0,0 +1,17 @@
> +ENTRY (foo);
> +SECTIONS
> +{
> +  .text : { *(.text) }
> +  .MIPS.stubs : { *(.MIPS.stubs) }
> +  HIDDEN (_gp = ALIGN(16) + 0x7ff0);
> +  .got : { *(.got) }
> +  .dynamic : { *(.dynamic) }
> +  .hash : { *(.hash) }
> +  .dynsym : { *(.dynsym) }
> +  .dynstr : { *(.dynstr) }
> +  .pdr : { *(.pdr) }
> +  .shstrtab : { *(.shstrtab) }
> +  .symtab : { *(.symtab) }
> +  .strtab : { *(.strtab) }
> +  /DISCARD/ : { *(*) }
> +}
> diff --git a/ld/testsuite/ld-mips-elf/mips-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
> index 50af78d1430..550f373efdc 100644
> --- a/ld/testsuite/ld-mips-elf/mips-elf.exp
> +++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
> @@ -719,6 +719,29 @@ if { $linux_gnu } {
>                     "readelf --relocs gp-hidden${suff64}.rd" \
>                     "readelf --syms gp-hidden.sd"] \
>                 "gp-hidden-${abi}"]]
> +
> +       set gotpc_asflags [regsub -- {-march=[^[:blank:]]*} $abi_asflags($abi) {}]
> +       run_ld_link_tests [list \
> +           [list \
> +               "MIPS R_MIPS_GOTPC support (Prepare, $abi)" \
> +               "$abi_ldflags($abi) -shared -T gotpc.ld" \
> +               "" \
> +               "$gotpc_asflags -mips64r6 -KPIC" \
> +               [list gotpc-callee.s] \
> +               "" \
> +               "libgotpc-callee-${abi}.so"]]
> +       run_ld_link_tests [list \
> +           [list \
> +               "MIPS R_MIPS_GOTPC support ($abi)" \
> +               "$abi_ldflags($abi) -shared -T gotpc.ld \
> +               -Ltmpdir -lgotpc-callee-${abi}" \
> +               "" \
> +               "$gotpc_asflags -mips64r6 -KPIC" \
> +               [list gotpc-${abi}.s] \
> +               [list \
> +                   "objdump -DTC gotpc-${abi}.dd" \
> +                   "readelf -A gotpc-${abi}.gd"] \
> +               "gotpc-${abi}.so"]]
>      }
>  }
>
> --
> 2.39.2
>

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

* Re: [PATCH v4] MIPS: Support PCREL GOT access
  2024-03-16  8:51 YunQiang Su
@ 2024-03-26  2:18 ` YunQiang Su
  2024-04-03  4:04 ` Fangrui Song
       [not found] ` <DS7PR12MB57650A5401095ED70D3AB0EDCB3D2@DS7PR12MB5765.namprd12.prod.outlook.com>
  2 siblings, 0 replies; 7+ messages in thread
From: YunQiang Su @ 2024-03-26  2:18 UTC (permalink / raw)
  To: nickc; +Cc: binutils, macro, xry111

@Maciej W. Rozycki ping.

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

* [PATCH v4] MIPS: Support PCREL GOT access
@ 2024-03-16  8:51 YunQiang Su
  2024-03-26  2:18 ` YunQiang Su
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: YunQiang Su @ 2024-03-16  8:51 UTC (permalink / raw)
  To: nickc; +Cc: binutils, macro, xry111

Current if we need to access a GOT entry, we use the
got_base + offset. Thus we have GOT and XGOT.
From MIPSr6, we have PCREL instructions like ALUIPC,
so we have no need to use got_base now.
For pre-R6, we can use BAL to get the the value of PC.

In this patch, we add 8 new relocs:
	R_MIPS_GOTPC_HI16, R_MIPS_GOTPC_LO16,
	R_MIPS_GOTPC_CALL_HI16, R_MIPS_GOTPC_CALL_LO16,
	R_MIPS_GOTPC_AHI16, R_MIPS_GOTPC_ALO16,
	R_MIPS_GOTPC_CALL_AHI16, R_MIPS_GOTPC_CALL_ALO16.
These asm notes can be used for them:
	%gotpc_hi(sym), %gotpc_lo(sym),
	%gotpc_call_hi(sym), %gotpc_call_lo(sym),
	%gotpc_ahi(sym), %gotpc_alo(sym),
	%gotpc_call_ahi(sym), %gotpc_call_alo(sym).

3 new BFD_RELOCS are added for ALUIPC style relocs:
	BFD_RELOC_MIPS_ALO16_GOTOFF,
	BFD_RELOC_MIPS_AHI16_GOTOFF,
	BFD_RELOC_MIPS_AHI16_S_GOTOFF.

6 new BFD_RELOCS are added for function calling:
	BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
	BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
	BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
	BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
	BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
	BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,

The mapping between BFD_RELOC_ and R_MIPS_GOTPC are:
	BFD_RELOC_HI16_S_GOTOFF -> R_MIPS_GOTPC_HI16
	BFD_RELOC_LO16_GOTOFF -> R_MIPS_GOTPC_LO16
	BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_HI16
	BFD_RELOC_MIPS_LO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_LO16.
	BFD_RELOC_MIPS_AHI16_S_GOTOFF -> R_MIPS_GOTPC_AHI16
	BFD_RELOC_MIPS_ALO16_GOTOFF -> R_MIPS_GOTPC_ALO16
	BFD_RELOC_MIPS_MIPS_AHI16_S_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_AHI16
	BFD_RELOC_MIPS_MIPS_ALO16_GOTOFF_CALL -> R_MIPS_GOTPC_CALL_ALO16.

The difference of BAL and ALUIPC is that ALUIPC will unset the lower 16
bits of result.

For r6, both styles are supported, and of course ALUIPC style is
recommended. For pre-R6, only BAL style is supported.

Here are the ASM examples:
ALUIPC:
	aluipc	$2,%gotpc_ahi(sym)
	lw	$2,%gotpc_alo(sym)($2)

BAL:
	bal	. + 8 # Note: $31 is clobbered here.
	lui	$2,%gotpc_hi(sym)
	addiu	$2,$2,$31
	lw	$2,%gotpc_lo(sym)($2)

gas/ChangeLog:
	* config/tc-mips.c: Add GOTPC relocs support.
	* testsuite/gas/mips/mips.exp: Add GOTPC testcases.
	* testsuite/gas/mips/gotpc-aluipc-32.s: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-64.s: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-n64.d: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-n32.d: Ditto.
	* testsuite/gas/mips/gotpc-aluipc-o32.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-32.s: Ditto.
	* testsuite/gas/mips/gotpc-bal-64.s: Ditto.
	* testsuite/gas/mips/gotpc-bal-n64.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-n32.d: Ditto.
	* testsuite/gas/mips/gotpc-bal-o32.d: Ditto.

bfd/ChangeLog:
	* elfxx-mips.c: Add GOTPC relocs support.
	* elf32-mips.c: Ditto.
	* elf64-mips.c: Ditto.
	* elfn32-mips.c: Ditto.
	* bfd-in2.h: Add new MIPS GOTPC BFD_RELOC defination.
	* reclos.c: Ditto.
	* libbfd.h: Ditto.

elfcpp/ChangeLog:
	* mips.h: Add new MIPS GOTPC relocs.

include/ChangeLog:
	* elf/mips.h (elf_mips_reloc_type): Add new MIPS GOTPC relocs.

ld/ChangeLog:
	* testsuite/ld-mips-elf/mips-elf.exp: Add GOTPC testcases.
	* testsuite/ld-mips-elf/gotpc-callee.s: Likewise.
	* testsuite/ld-mips-elf/gotpc.ld: Likewise.
	* testsuite/ld-mips-elf/gotpc-o32.s: Likewise.
	* testsuite/ld-mips-elf/gotpc-o32.dd: Likewise.
	* testsuite/ld-mips-elf/gotpc-o32.gd: Likewise.
	* testsuite/ld-mips-elf/gotpc-n32.s: Likewise.
	* testsuite/ld-mips-elf/gotpc-n32.dd: Likewise.
	* testsuite/ld-mips-elf/gotpc-n32.gd: Likewise.
	* testsuite/ld-mips-elf/gotpc-n64.s: Likewise.
	* testsuite/ld-mips-elf/gotpc-n64.dd: Likewise.
	* testsuite/ld-mips-elf/gotpc-n64.gd: Likewise.
---
 bfd/bfd-in2.h                             |  12 ++
 bfd/elf32-mips.c                          | 123 +++++++++++-
 bfd/elf64-mips.c                          | 234 +++++++++++++++++++++-
 bfd/elfn32-mips.c                         | 234 +++++++++++++++++++++-
 bfd/elfxx-mips.c                          | 107 +++++++++-
 bfd/libbfd.h                              |   9 +
 bfd/reloc.c                               |  18 ++
 elfcpp/mips.h                             |   8 +
 gas/config/tc-mips.c                      |  72 ++++++-
 gas/testsuite/gas/mips/gotpc-aluipc-32.s  |  51 +++++
 gas/testsuite/gas/mips/gotpc-aluipc-64.s  |  50 +++++
 gas/testsuite/gas/mips/gotpc-aluipc-n32.d |  17 ++
 gas/testsuite/gas/mips/gotpc-aluipc-n64.d |  25 +++
 gas/testsuite/gas/mips/gotpc-aluipc-o32.d |  17 ++
 gas/testsuite/gas/mips/gotpc-bal-32.s     |  55 +++++
 gas/testsuite/gas/mips/gotpc-bal-64.s     |  54 +++++
 gas/testsuite/gas/mips/gotpc-bal-n32.d    |  17 ++
 gas/testsuite/gas/mips/gotpc-bal-n64.d    |  25 +++
 gas/testsuite/gas/mips/gotpc-bal-o32.d    |  17 ++
 gas/testsuite/gas/mips/mips.exp           |   8 +
 include/elf/mips.h                        |  10 +-
 ld/testsuite/ld-mips-elf/gotpc-callee.s   |  41 ++++
 ld/testsuite/ld-mips-elf/gotpc-n32.dd     |  73 +++++++
 ld/testsuite/ld-mips-elf/gotpc-n32.gd     |  15 ++
 ld/testsuite/ld-mips-elf/gotpc-n32.s      |  46 +++++
 ld/testsuite/ld-mips-elf/gotpc-n64.dd     |  75 +++++++
 ld/testsuite/ld-mips-elf/gotpc-n64.gd     |  15 ++
 ld/testsuite/ld-mips-elf/gotpc-n64.s      |  46 +++++
 ld/testsuite/ld-mips-elf/gotpc-o32.dd     |  71 +++++++
 ld/testsuite/ld-mips-elf/gotpc-o32.gd     |  15 ++
 ld/testsuite/ld-mips-elf/gotpc-o32.s      |  45 +++++
 ld/testsuite/ld-mips-elf/gotpc.ld         |  17 ++
 ld/testsuite/ld-mips-elf/mips-elf.exp     |  23 +++
 33 files changed, 1637 insertions(+), 8 deletions(-)
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-32.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-64.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-n64.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-aluipc-o32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-32.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-64.s
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n32.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-n64.d
 create mode 100644 gas/testsuite/gas/mips/gotpc-bal-o32.d
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-callee.s
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.dd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.gd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n32.s
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.dd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.gd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-n64.s
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.dd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.gd
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc-o32.s
 create mode 100644 ld/testsuite/ld-mips-elf/gotpc.ld

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 29602e054da..e8bb1195c27 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -3658,6 +3658,18 @@ enum bfd_reloc_code_real
   BFD_RELOC_MIPS_18_PCREL_S3,
   BFD_RELOC_MIPS_19_PCREL_S2,
 
+  BFD_RELOC_MIPS_LO16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_HI16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL,
+
+  BFD_RELOC_MIPS_ALO16_GOTOFF,
+  BFD_RELOC_MIPS_AHI16_GOTOFF,
+  BFD_RELOC_MIPS_AHI16_S_GOTOFF,
+
+  BFD_RELOC_MIPS_ALO16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_AHI16_GOTOFF_CALL,
+  BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL,
+
   /* microMIPS versions of generic BFD relocs.  */
   BFD_RELOC_MICROMIPS_GPREL16,
   BFD_RELOC_MICROMIPS_HI16,
diff --git a/bfd/elf32-mips.c b/bfd/elf32-mips.c
index 63d7334dd52..7edcc30d331 100644
--- a/bfd/elf32-mips.c
+++ b/bfd/elf32-mips.c
@@ -809,6 +809,119 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
 	 0x0000ffff,		/* src_mask */
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The reloc used for BFD_RELOC_CTOR when doing a 64 bit link.  This
@@ -2030,7 +2143,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elf64-mips.c b/bfd/elf64-mips.c
index 489a461bb0b..8ebaa859475 100644
--- a/bfd/elf64-mips.c
+++ b/bfd/elf64-mips.c
@@ -889,6 +889,118 @@ static reloc_howto_type mips_elf64_howto_table_rel[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1670,6 +1782,118 @@ static reloc_howto_type mips_elf64_howto_table_rela[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 static reloc_howto_type mips16_elf64_howto_table_rel[] =
@@ -3743,7 +3967,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elfn32-mips.c b/bfd/elfn32-mips.c
index 7e672200006..f6a6560b4c2 100644
--- a/bfd/elfn32-mips.c
+++ b/bfd/elfn32-mips.c
@@ -868,6 +868,118 @@ static reloc_howto_type elf_mips_howto_table_rel[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 true,			/* partial_inplace */
+	 0x0000ffff,		/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 /* The relocation table used for SHT_RELA sections.  */
@@ -1650,6 +1762,118 @@ static reloc_howto_type elf_mips_howto_table_rela[] =
 	 0x0000ffff,		/* dst_mask */
 	 true),			/* pcrel_offset */
 
+  HOWTO (R_MIPS_GOTPC_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_HI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_LO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_HI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_HI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_LO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_LO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_AHI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_ALO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_AHI16,	/* type */
+	 16,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_signed, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_AHI16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
+  HOWTO (R_MIPS_GOTPC_CALL_ALO16,	/* type */
+	 0,			/* rightshift */
+	 4,			/* size */
+	 16,			/* bitsize */
+	 true,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 _bfd_mips_elf_generic_reloc,   /* special_function */
+	 "R_MIPS_GOTPC_CALL_ALO16",	/* name */
+	 false,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0x0000ffff,		/* dst_mask */
+	 true),			/* pcrel_offset */
+
 };
 
 static reloc_howto_type elf_mips16_howto_table_rel[] =
@@ -3577,7 +3801,15 @@ static const struct elf_reloc_map mips_reloc_map[] =
   { BFD_RELOC_MIPS_18_PCREL_S3, R_MIPS_PC18_S3 },
   { BFD_RELOC_MIPS_19_PCREL_S2, R_MIPS_PC19_S2 },
   { BFD_RELOC_HI16_S_PCREL, R_MIPS_PCHI16 },
-  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 }
+  { BFD_RELOC_LO16_PCREL, R_MIPS_PCLO16 },
+  { BFD_RELOC_HI16_S_GOTOFF, R_MIPS_GOTPC_HI16 },
+  { BFD_RELOC_LO16_GOTOFF, R_MIPS_GOTPC_LO16 },
+  { BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_HI16 },
+  { BFD_RELOC_MIPS_LO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_LO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF, R_MIPS_GOTPC_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF, R_MIPS_GOTPC_ALO16 },
+  { BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL, R_MIPS_GOTPC_CALL_AHI16 },
+  { BFD_RELOC_MIPS_ALO16_GOTOFF_CALL, R_MIPS_GOTPC_CALL_ALO16 }
 };
 
 static const struct elf_reloc_map mips16_reloc_map[] =
diff --git a/bfd/elfxx-mips.c b/bfd/elfxx-mips.c
index 89dd34e798b..c4d6787357e 100644
--- a/bfd/elfxx-mips.c
+++ b/bfd/elfxx-mips.c
@@ -2267,19 +2267,28 @@ got_page_reloc_p (unsigned int r_type)
 static inline bool
 got_lo16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_GOT_LO16 || r_type == R_MICROMIPS_GOT_LO16;
+  return (r_type == R_MIPS_GOT_LO16
+	  || r_type == R_MIPS_GOTPC_LO16
+	  || r_type == R_MIPS_GOTPC_ALO16
+	  || r_type == R_MICROMIPS_GOT_LO16);
 }
 
 static inline bool
 call_hi16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_CALL_HI16 || r_type == R_MICROMIPS_CALL_HI16;
+  return (r_type == R_MIPS_CALL_HI16
+	  || r_type == R_MIPS_GOTPC_CALL_HI16
+	  || r_type == R_MIPS_GOTPC_CALL_AHI16
+	  || r_type == R_MICROMIPS_CALL_HI16);
 }
 
 static inline bool
 call_lo16_reloc_p (unsigned int r_type)
 {
-  return r_type == R_MIPS_CALL_LO16 || r_type == R_MICROMIPS_CALL_LO16;
+  return (r_type == R_MIPS_CALL_LO16
+	  || r_type == R_MIPS_GOTPC_CALL_LO16
+	  || r_type == R_MIPS_GOTPC_CALL_ALO16
+	  || r_type == R_MICROMIPS_CALL_LO16);
 }
 
 static inline bool
@@ -5240,6 +5249,21 @@ mips_elf_highest (bfd_vma value ATTRIBUTE_UNUSED)
   return MINUS_ONE;
 #endif
 }
+
+/**/
+static bfd_vma
+mips_elf_16bit_align (bfd_vma value, bfd_vma p, bool hi)
+{
+  bfd_vma value_lo = (value & 0xffff) + (p & 0xffff);
+  bfd_vma value_hi = (value >> 16) & 0xffff;
+  value_hi += mips_elf_high (value_lo);
+  value_lo &= 0xffff;
+  if (hi)
+    return value_hi;
+  else
+    return value_lo;
+}
+
 \f
 /* Create the .compact_rel section.  */
 
@@ -5887,6 +5911,10 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS_GOT_DISP:
     case R_MIPS_GOT_LO16:
     case R_MIPS_CALL_LO16:
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_LO16:
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_ALO16:
     case R_MICROMIPS_CALL16:
     case R_MICROMIPS_GOT16:
     case R_MICROMIPS_GOT_PAGE:
@@ -5904,6 +5932,8 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       /* Fall through.  */
     case R_MIPS_GOT_HI16:
     case R_MIPS_CALL_HI16:
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_CALL_HI16:
     case R_MICROMIPS_GOT_HI16:
     case R_MICROMIPS_CALL_HI16:
       if (resolved_to_zero
@@ -5951,6 +5981,14 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
     case R_MIPS_CALL_HI16:
     case R_MIPS_GOT_LO16:
     case R_MIPS_CALL_LO16:
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_HI16:
+    case R_MIPS_GOTPC_CALL_LO16:
+    case R_MIPS_GOTPC_AHI16:
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_AHI16:
+    case R_MIPS_GOTPC_CALL_ALO16:
     case R_MICROMIPS_CALL16:
     case R_MICROMIPS_GOT16:
     case R_MICROMIPS_GOT_DISP:
@@ -6412,6 +6450,45 @@ mips_elf_calculate_relocation (bfd *abfd, bfd *input_bfd,
       value &= howto->dst_mask;
       break;
 
+    /* For r6 and pre-r6, we can use:
+	bal	. + 8
+	lui	$2,%gotpc_hi(sym) # or %gotpc_call_hi
+	addu	$2,$2,$ra
+	lw	$2,%gotpc_lo(sym) # or %gotpc_call_lo
+       In this case, the LO should +=4, and HI should -=4.
+
+       For R6+, we can use
+	aluipc	$2,%gotpc_ahi(sym) # or %gotpc_call_ahi
+	lw	$2,%gotpc_alo(sym)($2) or %gotpc_call_alo
+       In this case, the HI is OK, while LO should +4 and add the page_offet */
+    case R_MIPS_GOTPC_HI16:
+    case R_MIPS_GOTPC_CALL_HI16:
+      value = mips_elf_high (addend + gp - p + g - 4);
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_LO16:
+    case R_MIPS_GOTPC_CALL_LO16:
+      if (howto->partial_inplace)
+	addend = _bfd_mips_elf_sign_extend (addend, 16);
+      value = addend + gp - p + g + 4;
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_AHI16:
+    case R_MIPS_GOTPC_CALL_AHI16:
+      value = mips_elf_16bit_align (addend + gp - p + g, p, true);
+      value &= howto->dst_mask;
+      break;
+
+    case R_MIPS_GOTPC_ALO16:
+    case R_MIPS_GOTPC_CALL_ALO16:
+      if (howto->partial_inplace)
+	addend = _bfd_mips_elf_sign_extend (addend, 16);
+      value = mips_elf_16bit_align (addend + gp - p + g, p, false);
+      value &= howto->dst_mask;
+      break;
+
     case R_MIPS_PCHI16:
       value = mips_elf_high (symbol + addend - p);
       value &= howto->dst_mask;
@@ -8282,6 +8359,14 @@ mips_elf_add_lo16_rel_addend (bfd *abfd,
     lo16_type = R_MIPS16_LO16;
   else if (micromips_reloc_p (r_type))
     lo16_type = R_MICROMIPS_LO16;
+  else if (r_type == R_MIPS_GOTPC_HI16)
+    lo16_type = R_MIPS_GOTPC_LO16;
+  else if (r_type == R_MIPS_GOTPC_CALL_HI16)
+    lo16_type = R_MIPS_GOTPC_CALL_LO16;
+  else if (r_type == R_MIPS_GOTPC_AHI16)
+    lo16_type = R_MIPS_GOTPC_ALO16;
+  else if (r_type == R_MIPS_GOTPC_CALL_AHI16)
+    lo16_type = R_MIPS_GOTPC_CALL_ALO16;
   else if (r_type == R_MIPS_PCHI16)
     lo16_type = R_MIPS_PCLO16;
   else
@@ -8742,6 +8827,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MIPS_CALL16:
 	case R_MIPS_CALL_HI16:
 	case R_MIPS_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_HI16:
+	case R_MIPS_GOTPC_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_AHI16:
+	case R_MIPS_GOTPC_CALL_ALO16:
 	case R_MIPS16_CALL16:
 	case R_MICROMIPS_CALL16:
 	case R_MICROMIPS_CALL_HI16:
@@ -8751,6 +8840,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_MIPS_GOT16:
 	case R_MIPS_GOT_LO16:
+	case R_MIPS_GOTPC_LO16:
+	case R_MIPS_GOTPC_ALO16:
 	case R_MIPS_GOT_PAGE:
 	case R_MIPS_GOT_DISP:
 	case R_MIPS16_GOT16:
@@ -8788,6 +8879,8 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	  /* Fall through.  */
 	case R_MIPS_GOT_HI16:
+	case R_MIPS_GOTPC_HI16:
+	case R_MIPS_GOTPC_AHI16:
 	case R_MIPS_GOT_OFST:
 	case R_MIPS_TLS_GOTTPREL:
 	case R_MIPS_TLS_GD:
@@ -8958,6 +9051,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
 	case R_MIPS_CALL_HI16:
 	case R_MIPS_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_HI16:
+	case R_MIPS_GOTPC_CALL_LO16:
+	case R_MIPS_GOTPC_CALL_AHI16:
+	case R_MIPS_GOTPC_CALL_ALO16:
 	case R_MICROMIPS_CALL_HI16:
 	case R_MICROMIPS_CALL_LO16:
 	  if (h != NULL)
@@ -8983,6 +9080,10 @@ _bfd_mips_elf_check_relocs (bfd *abfd, struct bfd_link_info *info,
 	case R_MIPS_GOT16:
 	case R_MIPS_GOT_HI16:
 	case R_MIPS_GOT_LO16:
+	case R_MIPS_GOTPC_HI16:
+	case R_MIPS_GOTPC_LO16:
+	case R_MIPS_GOTPC_AHI16:
+	case R_MIPS_GOTPC_ALO16:
 	case R_MICROMIPS_GOT16:
 	case R_MICROMIPS_GOT_HI16:
 	case R_MICROMIPS_GOT_LO16:
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index f15b5f27db8..c10ee485abf 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -1273,6 +1273,15 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_MIPS_26_PCREL_S2",
   "BFD_RELOC_MIPS_18_PCREL_S3",
   "BFD_RELOC_MIPS_19_PCREL_S2",
+  "BFD_RELOC_MIPS_LO16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_HI16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_ALO16_GOTOFF",
+  "BFD_RELOC_MIPS_AHI16_GOTOFF",
+  "BFD_RELOC_MIPS_AHI16_S_GOTOFF",
+  "BFD_RELOC_MIPS_ALO16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_AHI16_GOTOFF_CALL",
+  "BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL",
   "BFD_RELOC_MICROMIPS_GPREL16",
   "BFD_RELOC_MICROMIPS_HI16",
   "BFD_RELOC_MICROMIPS_HI16_S",
diff --git a/bfd/reloc.c b/bfd/reloc.c
index a187afe9b56..b88763c850b 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -2040,6 +2040,24 @@ ENUMX
   BFD_RELOC_MIPS_18_PCREL_S3
 ENUMX
   BFD_RELOC_MIPS_19_PCREL_S2
+ENUMX
+  BFD_RELOC_LO16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_HI16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_HI16_S_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_ALO16_GOTOFF
+ENUMX
+  BFD_RELOC_AHI16_GOTOFF
+ENUMX
+  BFD_RELOC_AHI16_S_GOTOFF
+ENUMX
+  BFD_RELOC_ALO16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_AHI16_GOTOFF_CALL
+ENUMX
+  BFD_RELOC_AHI16_S_GOTOFF_CALL
 ENUMDOC
   MIPS PC-relative relocations.
 
diff --git a/elfcpp/mips.h b/elfcpp/mips.h
index e8a8e2458e9..79884bd651b 100644
--- a/elfcpp/mips.h
+++ b/elfcpp/mips.h
@@ -104,6 +104,14 @@ enum
   R_MIPS_PC19_S2 = 63,
   R_MIPS_PCHI16 = 64,
   R_MIPS_PCLO16 = 65,
+  R_MIPS_GOTPC_HI16 = 66,
+  R_MIPS_GOTPC_LO16 = 67,
+  R_MIPS_GOTPC_CALL_HI16 = 68,
+  R_MIPS_GOTPC_CALL_LO16 = 69,
+  R_MIPS_GOTPC_AHI16 = 70,
+  R_MIPS_GOTPC_ALO16 = 71,
+  R_MIPS_GOTPC_CALL_AHI16 = 72,
+  R_MIPS_GOTPC_CALL_ALO16 = 73,
   // These relocs are used for the mips16.
   R_MIPS16_26 = 100,
   R_MIPS16_GPREL = 101,
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
index 8f54cb8937a..7ee6c0259fa 100644
--- a/gas/config/tc-mips.c
+++ b/gas/config/tc-mips.c
@@ -4372,6 +4372,14 @@ limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
     case BFD_RELOC_32_PCREL:
     case BFD_RELOC_HI16_S_PCREL:
     case BFD_RELOC_LO16_PCREL:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
       return HAVE_64BIT_ADDRESSES;
 
     default:
@@ -7432,6 +7440,8 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
 
     case BFD_RELOC_HI16_S:
     case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
     case BFD_RELOC_MICROMIPS_HI16_S:
     case BFD_RELOC_MIPS16_HI16_S:
       *result = ((operand + 0x8000) >> 16) & 0xffff;
@@ -7445,6 +7455,10 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
 
     case BFD_RELOC_LO16:
     case BFD_RELOC_LO16_PCREL:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
     case BFD_RELOC_MICROMIPS_LO16:
     case BFD_RELOC_MIPS16_LO16:
       *result = operand & 0xffff;
@@ -7507,6 +7521,22 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
 	     (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
 
+  if (!ISA_IS_R6 (mips_opts.isa))
+    switch (*reloc_type)
+      {
+	default:
+	  break;
+
+	case BFD_RELOC_MIPS_ALO16_GOTOFF:
+	case BFD_RELOC_MIPS_AHI16_GOTOFF:
+	case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+	case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
+	case BFD_RELOC_MIPS_AHI16_GOTOFF_CALL:
+	case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+	  as_fatal (_("ALUIPC style GOTPC cannot work with pre-R6."));
+	  break;
+      }
+
   if (address_expr == NULL)
     ip->complete_p = 1;
   else if (reloc_type[0] <= BFD_RELOC_UNUSED
@@ -14583,7 +14613,15 @@ static const struct percent_op_match mips_percent_op[] =
   {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
   {"%hi", BFD_RELOC_HI16_S},
   {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
-  {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
+  {"%pcrel_lo", BFD_RELOC_LO16_PCREL},
+  {"%gotpc_hi", BFD_RELOC_HI16_S_GOTOFF},
+  {"%gotpc_lo", BFD_RELOC_LO16_GOTOFF},
+  {"%gotpc_call_hi", BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL},
+  {"%gotpc_call_lo", BFD_RELOC_MIPS_LO16_GOTOFF_CALL},
+  {"%gotpc_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF},
+  {"%gotpc_alo", BFD_RELOC_MIPS_ALO16_GOTOFF},
+  {"%gotpc_call_ahi", BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL},
+  {"%gotpc_call_alo", BFD_RELOC_MIPS_ALO16_GOTOFF_CALL}
 };
 
 static const struct percent_op_match mips16_percent_op[] =
@@ -15543,6 +15581,14 @@ mips_force_relocation (fixS *fixp)
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
 	  || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+	  || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
+	  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
 	  || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
 	  || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
     return 1;
@@ -15824,6 +15870,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       case BFD_RELOC_MIPS_19_PCREL_S2:
       case BFD_RELOC_HI16_S_PCREL:
       case BFD_RELOC_LO16_PCREL:
+      case BFD_RELOC_HI16_S_GOTOFF:
+      case BFD_RELOC_LO16_GOTOFF:
+      case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+      case BFD_RELOC_MIPS_ALO16_GOTOFF:
+      case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+      case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
 	break;
 
       case BFD_RELOC_32:
@@ -15967,6 +16021,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_LO16_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF:
+    case BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL:
+    case BFD_RELOC_MIPS_ALO16_GOTOFF_CALL:
     case BFD_RELOC_HI16_S_PCREL:
     case BFD_RELOC_LO16_PCREL:
     case BFD_RELOC_MIPS16_GPREL:
@@ -18388,6 +18450,14 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
 		  || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+		  || fixp->fx_r_type == BFD_RELOC_HI16_S_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_LO16_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_LO16_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_HI16_S_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_ALO16_GOTOFF_CALL
+		  || fixp->fx_r_type == BFD_RELOC_MIPS_AHI16_S_GOTOFF_CALL
 		  || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
 		  || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
 
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-32.s b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
new file mode 100644
index 00000000000..877193313a1
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-32.s
@@ -0,0 +1,51 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	2008
+	.module	fp=xx
+	.module	nooddspreg
+	.module	arch=mips32r6
+	.abicalls
+	.text
+	.section	.rodata.str1.4,"aMS",@progbits,1
+	.align	2
+$LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,32,$31		# vars= 0, regs= 1/0, args= 16, gp= 8
+	.mask	0x80000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload	$25
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+
+	aluipc	$4,%gotpc_ahi($LC0)
+	lw	$4,%gotpc_alo($LC0)($4)
+
+	aluipc	$25,%gotpc_call_ahi(puts)
+	lw	$25,%gotpc_call_alo(puts)($25)
+
+	.cprestore	16
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	lw	$31,28($sp)
+	move	$2,$0
+	jr	$31
+	addiu	$sp,$sp,32
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (Debian 12.2.0-14) 12.2.0"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-64.s b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
new file mode 100644
index 00000000000..234be37b6fe
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-64.s
@@ -0,0 +1,50 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi64
+	.previous
+	.abicalls
+	.text
+	.section	.rodata.str1.8,"aMS",@progbits,1
+	.align	3
+.LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.align	3
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,16,$31		# vars= 0, regs= 2/0, args= 0, gp= 0
+	.mask	0x90000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	daddiu	$sp,$sp,-16
+	sd	$28,0($sp)
+	lui	$28,%hi(%neg(%gp_rel(main)))
+	daddu	$28,$28,$25
+	sd	$31,8($sp)
+	daddiu	$28,$28,%lo(%neg(%gp_rel(main)))
+
+	aluipc	$4,%gotpc_ahi(.LC0)
+	ld	$4,%gotpc_alo(.LC0)($4)
+
+	aluipc	$25,%gotpc_call_ahi(puts)
+	ld	$25,%gotpc_call_alo(puts)($25)
+
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	ld	$31,8($sp)
+	ld	$28,0($sp)
+	move	$2,$0
+	jr	$31
+	daddiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n32.d b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
new file mode 100644
index 00000000000..41bc6d474d7
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-n32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N32)
+#source: gotpc-aluipc-64.s
+#as: -n32 -mips64r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_ALO16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-n64.d b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
new file mode 100644
index 00000000000..0561040ef09
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-n64.d
@@ -0,0 +1,25 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, ALUIPC, N64)
+#source: gotpc-aluipc-64.s
+#as: -64 -mips64r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.LC0
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_ALO16	\.LC0
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-aluipc-o32.d b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
new file mode 100644
index 00000000000..1832061a34e
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-aluipc-o32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (OldABI, ALUIPC, O32)
+#source: gotpc-aluipc-32.s
+#as: -32 -mips32r6
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_AHI16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_ALO16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_CALL_AHI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_ALO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-32.s b/gas/testsuite/gas/mips/gotpc-bal-32.s
new file mode 100644
index 00000000000..841a13dbf87
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-32.s
@@ -0,0 +1,55 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi32
+	.previous
+	.nan	2008
+	.module	fp=xx
+	.module	nooddspreg
+	.module	arch=mips32r6
+	.abicalls
+	.text
+	.section	.rodata.str1.4,"aMS",@progbits,1
+	.align	2
+$LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,32,$31		# vars= 0, regs= 1/0, args= 16, gp= 8
+	.mask	0x80000000,-4
+	.fmask	0x00000000,0
+	.set	noreorder
+	.cpload	$25
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+
+	bal	. + 8
+	lui	$4,%gotpc_hi($LC0)
+	addu	$4,$4,$31
+	lw	$4,%gotpc_lo($LC0)($4)
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(puts)
+	addu	$25,$25,$31
+	lw	$25,%gotpc_call_lo(puts)($25)
+
+	.cprestore	16
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	lw	$31,28($sp)
+	move	$2,$0
+	jr	$31
+	addiu	$sp,$sp,32
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.ident	"GCC: (Debian 12.2.0-14) 12.2.0"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-bal-64.s b/gas/testsuite/gas/mips/gotpc-bal-64.s
new file mode 100644
index 00000000000..c97e6b87af9
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-64.s
@@ -0,0 +1,54 @@
+	.file	1 "nn.c"
+	.section .mdebug.abi64
+	.previous
+	.abicalls
+	.text
+	.section	.rodata.str1.8,"aMS",@progbits,1
+	.align	3
+.LC0:
+	.ascii	"XXXX\000"
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.align	3
+	.globl	main
+	.set	nomips16
+	.ent	main
+	.type	main, @function
+main:
+	.frame	$sp,16,$31		# vars= 0, regs= 2/0, args= 0, gp= 0
+	.mask	0x90000000,-8
+	.fmask	0x00000000,0
+	.set	noreorder
+	.set	nomacro
+	daddiu	$sp,$sp,-16
+	sd	$28,0($sp)
+	lui	$28,%hi(%neg(%gp_rel(main)))
+	daddu	$28,$28,$25
+	sd	$31,8($sp)
+	daddiu	$28,$28,%lo(%neg(%gp_rel(main)))
+
+	bal	. + 8
+	lui	$4,%gotpc_hi(.LC0)
+	daddu	$4,$4,$31
+	ld	$4,%gotpc_lo(.LC0)($4)
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(puts)
+	daddu	$25,$25,$31
+	ld	$25,%gotpc_call_lo(puts)($25)
+
+	.reloc	1f,R_MIPS_JALR,puts
+1:	jalr	$25
+	nop
+
+	ld	$31,8($sp)
+	ld	$28,0($sp)
+	move	$2,$0
+	jr	$31
+	daddiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	main
+	.size	main, .-main
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gas/testsuite/gas/mips/gotpc-bal-n32.d b/gas/testsuite/gas/mips/gotpc-bal-n32.d
new file mode 100644
index 00000000000..ec412020dc7
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-n32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, BAL, N32)
+#source: gotpc-bal-64.s
+#as: -n32 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.8
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-n64.d b/gas/testsuite/gas/mips/gotpc-bal-n64.d
new file mode 100644
index 00000000000..60b9f020f50
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-n64.d
@@ -0,0 +1,25 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (NewABI, BAL, N64)
+#source: gotpc-bal-64.s
+#as: -64 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.8
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.8
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+.*R_MIPS_NONE.*
+.*R_MIPS_NONE.*
+#pass
diff --git a/gas/testsuite/gas/mips/gotpc-bal-o32.d b/gas/testsuite/gas/mips/gotpc-bal-o32.d
new file mode 100644
index 00000000000..57cb891042a
--- /dev/null
+++ b/gas/testsuite/gas/mips/gotpc-bal-o32.d
@@ -0,0 +1,17 @@
+#objdump: -dr --prefix-addresses --show-raw-insn
+#name: R_MIPS_GOTPC support (OldABI, BAL, O32)
+#source: gotpc-bal-32.s
+#as: -32 -march=from-abi
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text\.startup:
+#...
+.*R_MIPS_GOTPC_HI16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_LO16	\.rodata\.str1\.4
+#...
+.*R_MIPS_GOTPC_CALL_HI16	puts
+#...
+.*R_MIPS_GOTPC_CALL_LO16	puts
+#pass
diff --git a/gas/testsuite/gas/mips/mips.exp b/gas/testsuite/gas/mips/mips.exp
index 2ddbf0c768d..619d04b3bd5 100644
--- a/gas/testsuite/gas/mips/mips.exp
+++ b/gas/testsuite/gas/mips/mips.exp
@@ -1210,7 +1210,15 @@ if { [istarget mips*-*-vxworks*] } {
 	run_dump_test "elf-rel-xgot-n32"
 	run_dump_test "elf-rel-got-n64"
 	run_dump_test "elf-rel-xgot-n64"
+
+	run_dump_test "gotpc-bal-n64"
+	run_dump_test "gotpc-bal-n32"
+	run_dump_test "gotpc-aluipc-n64"
+	run_dump_test "gotpc-aluipc-n32"
     }
+    run_dump_test "gotpc-bal-o32"
+    run_dump_test "gotpc-aluipc-o32"
+
     run_dump_test "elf-rel17"
     if $has_newabi {
 	run_dump_test "elf-rel18"
diff --git a/include/elf/mips.h b/include/elf/mips.h
index 686d5500e02..23e95fe0c45 100644
--- a/include/elf/mips.h
+++ b/include/elf/mips.h
@@ -98,7 +98,15 @@ START_RELOC_NUMBERS (elf_mips_reloc_type)
   RELOC_NUMBER (R_MIPS_PC19_S2, 63)
   RELOC_NUMBER (R_MIPS_PCHI16, 64)
   RELOC_NUMBER (R_MIPS_PCLO16, 65)
-  FAKE_RELOC (R_MIPS_max, 66)
+  RELOC_NUMBER (R_MIPS_GOTPC_HI16, 66)
+  RELOC_NUMBER (R_MIPS_GOTPC_LO16, 67)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_HI16, 68)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_LO16, 69)
+  RELOC_NUMBER (R_MIPS_GOTPC_AHI16, 70)
+  RELOC_NUMBER (R_MIPS_GOTPC_ALO16, 71)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_AHI16, 72)
+  RELOC_NUMBER (R_MIPS_GOTPC_CALL_ALO16, 73)
+  FAKE_RELOC (R_MIPS_max, 74)
   /* These relocs are used for the mips16.  */
   FAKE_RELOC (R_MIPS16_min, 100)
   RELOC_NUMBER (R_MIPS16_26, 100)
diff --git a/ld/testsuite/ld-mips-elf/gotpc-callee.s b/ld/testsuite/ld-mips-elf/gotpc-callee.s
new file mode 100644
index 00000000000..4689b66f302
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-callee.s
@@ -0,0 +1,41 @@
+	.text
+	.set	nomips16
+	.set	nomicromips
+	.globl	c1
+	.ent	c1
+	.type	c1, @function
+c1:
+	.set	noreorder
+	.set	nomacro
+	jr	$31
+	li	$2,1
+	.set	reorder
+	.set	macro
+	.end	c1
+	.size	c1, .-c1
+
+	.globl	c2
+	.ent	c2
+	.type	c2, @function
+c2:
+	.set	noreorder
+	.set	nomacro
+	jr	$31
+	li	$2,2
+	.set	reorder
+	.set	macro
+	.end	c2
+	.size	c2, .-c2
+
+	.data
+	.globl	g1
+	.type	g1, @object
+	.size	g1, 4
+g1:
+	.word	0x12345678
+
+	.globl	g2
+	.type	g2, @object
+	.size	g2, 4
+g2:
+	.word	0x09abcdef
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.dd b/ld/testsuite/ld-mips-elf/gotpc-n32.dd
new file mode 100644
index 00000000000..2c94d04ee27
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n32.dd
@@ -0,0 +1,73 @@
+
+.*: +file format .*mips.*
+
+DYNAMIC SYMBOL TABLE:
+00000000 g    DF .text	00000074 f1
+00000090      DF \*UND\*	00000000 c1
+00000004 g    DO \*ABS\*	00000004 g2
+00000080      DF \*UND\*	00000000 c2
+00000000 g    DO \*ABS\*	00000004 g1
+
+
+
+Disassembly of section .text:
+
+00000000 <f1>:
+   0:	27bdfff0 	addiu	sp,sp,-16
+   4:	ffbf0008 	sd	ra,8\(sp\)
+   8:	ffb00000 	sd	s0,0\(sp\)
+   c:	24100000 	li	s0,0
+
+00000010 <L0\^A>:
+  10:	04110001 	bal	18 <L0\^A\+0x8>
+  14:	3c190000 	lui	t9,0x0
+  18:	033fc821 	addu	t9,t9,ra
+  1c:	8f3900a0 	lw	t9,160\(t9\)
+  20:	0320f809 	jalr	t9
+  24:	00000000 	nop
+  28:	02028021 	addu	s0,s0,v0
+  2c:	ef3f0000 	aluipc	t9,0x0
+  30:	8f3900c0 	lw	t9,192\(t9\)
+  34:	0320f809 	jalr	t9
+  38:	00000000 	nop
+  3c:	02028021 	addu	s0,s0,v0
+
+00000040 <L0\^A>:
+  40:	04110001 	bal	48 <L0\^A\+0x8>
+  44:	3c020000 	lui	v0,0x0
+  48:	005f1021 	addu	v0,v0,ra
+  4c:	8c42007c 	lw	v0,124\(v0\)
+  50:	00501021 	addu	v0,v0,s0
+  54:	ec7f0000 	aluipc	v1,0x0
+  58:	8c6300bc 	lw	v1,188\(v1\)
+  5c:	00431021 	addu	v0,v0,v1
+  60:	dfb00000 	ld	s0,0\(sp\)
+  64:	dfbf0008 	ld	ra,8\(sp\)
+  68:	24420001 	addiu	v0,v0,1
+  6c:	03e00009 	jr	ra
+  70:	27bd0010 	addiu	sp,sp,16
+	...
+
+Disassembly of section .MIPS.stubs:
+
+00000080 <_MIPS_STUBS_>:
+  80:	8f998010 	lw	t9,-32752\(gp\)
+  84:	03e07825 	move	t3,ra
+  88:	0320f809 	jalr	t9
+  8c:	24180004 	li	t8,4
+  90:	8f998010 	lw	t9,-32752\(gp\)
+  94:	03e07825 	move	t3,ra
+  98:	0320f809 	jalr	t9
+  9c:	24180002 	li	t8,2
+	...
+
+Disassembly of section .got:
+
+000000b0 <.got>:
+  b0:	00000000 	nop
+  b4:	80000000 	lb	zero,0\(zero\)
+  b8:	00000090 	.word	0x90
+  bc:	00000004 	sllv	zero,zero,zero
+  c0:	00000080 	sll	zero,zero,0x2
+  c4:	00000000 	nop
+#...
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.gd b/ld/testsuite/ld-mips-elf/gotpc-n32.gd
new file mode 100644
index 00000000000..33c9b40ab77
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n32.gd
@@ -0,0 +1,15 @@
+
+Primary GOT:
+ Canonical gp value: 000080a0
+
+ Reserved entries:
+   Address     Access  Initial Purpose
+  000000b0 -32752\(gp\) 00000000 Lazy resolver
+  000000b4 -32748\(gp\) 80000000 Module pointer \(GNU extension\)
+
+ Global entries:
+   Address     Access  Initial Sym.Val. Type    Ndx Name
+  000000b8 -32744\(gp\) 00000090 00000090 FUNC    UND c1
+  000000bc -32740\(gp\) 00000004 00000004 OBJECT  ABS g2
+  000000c0 -32736\(gp\) 00000080 00000080 FUNC    UND c2
+  000000c4 -32732\(gp\) 00000000 00000000 OBJECT  ABS g1
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n32.s b/ld/testsuite/ld-mips-elf/gotpc-n32.s
new file mode 100644
index 00000000000..ce4f4af87f1
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n32.s
@@ -0,0 +1,46 @@
+	.text
+	.globl	f1
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1
+	.type	f1, @function
+f1:
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-16
+	sd	$31,8($sp)
+	sd	$16,0($sp)
+	li	$16,0
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(c1)
+	addu	$25,$25,$31
+	lw	$25,%gotpc_call_lo(c1)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+	aluipc	$25,%gotpc_call_ahi(c2)
+	lw	$25,%gotpc_call_alo(c2)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+
+	bal	. + 8
+	lui	$2,%gotpc_hi(g1)
+	addu	$2,$2,$31
+	lw	$2,%gotpc_lo(g1)($2)
+	addu	$2,$2,$16
+	aluipc	$3,%gotpc_ahi(g2)
+	lw	$3,%gotpc_alo(g2)($3)
+	addu	$2,$2,$3
+
+	ld	$16,0($sp)
+	ld	$31,8($sp)
+	addiu	$2,$2,1
+	jr	$31
+	addiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	f1
+	.size	f1, .-f1
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.dd b/ld/testsuite/ld-mips-elf/gotpc-n64.dd
new file mode 100644
index 00000000000..08e8e2b48a1
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n64.dd
@@ -0,0 +1,75 @@
+
+.*: +file format .*mips.*
+
+DYNAMIC SYMBOL TABLE:
+0000000000000000 g    DF .text	0000000000000074 f1
+0000000000000090      DF \*UND\*	0000000000000000 c1
+0000000000000004 g    DO \*ABS\*	0000000000000004 g2
+0000000000000080      DF \*UND\*	0000000000000000 c2
+0000000000000000 g    DO \*ABS\*	0000000000000004 g1
+
+
+
+Disassembly of section .text:
+
+0000000000000000 <f1>:
+   0:	67bdfff0 	daddiu	sp,sp,-16
+   4:	ffbf0008 	sd	ra,8\(sp\)
+   8:	ffb00000 	sd	s0,0\(sp\)
+   c:	24100000 	li	s0,0
+
+0000000000000010 <L0\^A>:
+  10:	04110001 	bal	18 <L0\^A\+0x8>
+  14:	3c190000 	lui	t9,0x0
+  18:	033fc82d 	daddu	t9,t9,ra
+  1c:	df3900a8 	ld	t9,168\(t9\)
+  20:	0320f809 	jalr	t9
+  24:	00000000 	nop
+  28:	02028021 	addu	s0,s0,v0
+  2c:	ef3f0000 	aluipc	t9,0x0
+  30:	df3900d0 	ld	t9,208\(t9\)
+  34:	0320f809 	jalr	t9
+  38:	00000000 	nop
+  3c:	02028021 	addu	s0,s0,v0
+
+0000000000000040 <L0\^A>:
+  40:	04110001 	bal	48 <L0\^A\+0x8>
+  44:	3c020000 	lui	v0,0x0
+  48:	005f102d 	daddu	v0,v0,ra
+  4c:	8c420090 	lw	v0,144\(v0\)
+  50:	00501021 	addu	v0,v0,s0
+  54:	ec7f0000 	aluipc	v1,0x0
+  58:	8c6300c8 	lw	v1,200\(v1\)
+  5c:	00431021 	addu	v0,v0,v1
+  60:	dfb00000 	ld	s0,0\(sp\)
+  64:	dfbf0008 	ld	ra,8\(sp\)
+  68:	24420001 	addiu	v0,v0,1
+  6c:	03e00009 	jr	ra
+  70:	67bd0010 	daddiu	sp,sp,16
+	...
+
+Disassembly of section .MIPS.stubs:
+
+0000000000000080 <_MIPS_STUBS_>:
+  80:	df998010 	ld	t9,-32752\(gp\)
+  84:	03e07825 	move	t3,ra
+  88:	0320f809 	jalr	t9
+  8c:	64180004 	daddiu	t8,zero,4
+  90:	df998010 	ld	t9,-32752\(gp\)
+  94:	03e07825 	move	t3,ra
+  98:	0320f809 	jalr	t9
+  9c:	64180002 	daddiu	t8,zero,2
+	...
+
+Disassembly of section .got:
+
+00000000000000b0 <.got>:
+	...
+  bc:	80000000 	lb	zero,0\(zero\)
+  c0:	00000090 	.word	0x90
+  c4:	00000000 	nop
+  c8:	00000004 	sllv	zero,zero,zero
+  cc:	00000000 	nop
+  d0:	00000080 	sll	zero,zero,0x2
+	...
+#...
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.gd b/ld/testsuite/ld-mips-elf/gotpc-n64.gd
new file mode 100644
index 00000000000..c08cca49ba4
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n64.gd
@@ -0,0 +1,15 @@
+
+Primary GOT:
+ Canonical gp value: 00000000000080a0
+
+ Reserved entries:
+           Address     Access          Initial Purpose
+  00000000000000b0 -32752\(gp\) 0000000000000000 Lazy resolver
+  00000000000000b8 -32744\(gp\) 8000000000000000 Module pointer \(GNU extension\)
+
+ Global entries:
+           Address     Access          Initial         Sym.Val. Type    Ndx Name
+  00000000000000c0 -32736\(gp\) 0000000000000090 0000000000000090 FUNC    UND c1
+  00000000000000c8 -32728\(gp\) 0000000000000004 0000000000000004 OBJECT  ABS g2
+  00000000000000d0 -32720\(gp\) 0000000000000080 0000000000000080 FUNC    UND c2
+  00000000000000d8 -32712\(gp\) 0000000000000000 0000000000000000 OBJECT  ABS g1
diff --git a/ld/testsuite/ld-mips-elf/gotpc-n64.s b/ld/testsuite/ld-mips-elf/gotpc-n64.s
new file mode 100644
index 00000000000..47dbd40af2b
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-n64.s
@@ -0,0 +1,46 @@
+	.text
+	.globl	f1
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1
+	.type	f1, @function
+f1:
+	.set	noreorder
+	.set	nomacro
+	daddiu	$sp,$sp,-16
+	sd	$31,8($sp)
+	sd	$16,0($sp)
+	li	$16,0
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(c1)
+	daddu	$25,$25,$31
+	ld	$25,%gotpc_call_lo(c1)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+	aluipc	$25,%gotpc_call_ahi(c2)
+	ld	$25,%gotpc_call_alo(c2)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+
+	bal	. + 8
+	lui	$2,%gotpc_hi(g1)
+	daddu	$2,$2,$31
+	lw	$2,%gotpc_lo(g1)($2)
+	addu	$2,$2,$16
+	aluipc	$3,%gotpc_ahi(g2)
+	lw	$3,%gotpc_alo(g2)($3)
+	addu	$2,$2,$3
+
+	ld	$16,0($sp)
+	ld	$31,8($sp)
+	addiu	$2,$2,1
+	jr	$31
+	daddiu	$sp,$sp,16
+
+	.set	macro
+	.set	reorder
+	.end	f1
+	.size	f1, .-f1
diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.dd b/ld/testsuite/ld-mips-elf/gotpc-o32.dd
new file mode 100644
index 00000000000..1c77b059ef6
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-o32.dd
@@ -0,0 +1,71 @@
+
+.*: +file format .*mips.*
+
+DYNAMIC SYMBOL TABLE:
+00000000 g    DF .text	00000070 f1
+00000080      DF \*UND\*	00000000 c1
+00000004 g    DO \*ABS\*	00000004 g2
+00000070      DF \*UND\*	00000000 c2
+00000000 g    DO \*ABS\*	00000004 g1
+
+
+
+Disassembly of section .text:
+
+00000000 <f1>:
+   0:	27bdffe0 	addiu	sp,sp,-32
+   4:	afbf001c 	sw	ra,28\(sp\)
+   8:	afb00018 	sw	s0,24\(sp\)
+   c:	24100000 	li	s0,0
+
+00000010 <L0\^A>:
+  10:	04110001 	bal	18 <L0\^A\+0x8>
+  14:	3c190000 	lui	t9,0x0
+  18:	033fc821 	addu	t9,t9,ra
+  1c:	8f390090 	lw	t9,144\(t9\)
+  20:	0320f809 	jalr	t9
+  24:	00000000 	nop
+  28:	02028021 	addu	s0,s0,v0
+  2c:	ef3f0000 	aluipc	t9,0x0
+  30:	8f3900b0 	lw	t9,176\(t9\)
+  34:	0320f809 	jalr	t9
+  38:	00000000 	nop
+  3c:	02028021 	addu	s0,s0,v0
+
+00000040 <L0\^A>:
+  40:	04110001 	bal	48 <L0\^A\+0x8>
+  44:	3c020000 	lui	v0,0x0
+  48:	005f1021 	addu	v0,v0,ra
+  4c:	8c42006c 	lw	v0,108\(v0\)
+  50:	00501021 	addu	v0,v0,s0
+  54:	ec7f0000 	aluipc	v1,0x0
+  58:	8c6300ac 	lw	v1,172\(v1\)
+  5c:	00431021 	addu	v0,v0,v1
+  60:	8fb00018 	lw	s0,24\(sp\)
+  64:	8fbf001c 	lw	ra,28\(sp\)
+  68:	03e00009 	jr	ra
+  6c:	27bd0020 	addiu	sp,sp,32
+
+Disassembly of section .MIPS.stubs:
+
+00000070 <_MIPS_STUBS_>:
+  70:	8f998010 	lw	t9,-32752\(gp\)
+  74:	03e07825 	move	t7,ra
+  78:	0320f809 	jalr	t9
+  7c:	24180004 	li	t8,4
+  80:	8f998010 	lw	t9,-32752\(gp\)
+  84:	03e07825 	move	t7,ra
+  88:	0320f809 	jalr	t9
+  8c:	24180002 	li	t8,2
+	...
+
+Disassembly of section .got:
+
+000000a0 <.got>:
+  a0:	00000000 	nop
+  a4:	80000000 	lb	zero,0\(zero\)
+  a8:	00000080 	sll	zero,zero,0x2
+  ac:	00000004 	sllv	zero,zero,zero
+  b0:	00000070 	tge	zero,zero,0x1
+  b4:	00000000 	nop
+#...
diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.gd b/ld/testsuite/ld-mips-elf/gotpc-o32.gd
new file mode 100644
index 00000000000..7a479237a22
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-o32.gd
@@ -0,0 +1,15 @@
+
+Primary GOT:
+ Canonical gp value: 00008090
+
+ Reserved entries:
+   Address     Access  Initial Purpose
+  000000a0 -32752\(gp\) 00000000 Lazy resolver
+  000000a4 -32748\(gp\) 80000000 Module pointer \(GNU extension\)
+
+ Global entries:
+   Address     Access  Initial Sym.Val. Type    Ndx Name
+  000000a8 -32744\(gp\) 00000080 00000080 FUNC    UND c1
+  000000ac -32740\(gp\) 00000004 00000004 OBJECT  ABS g2
+  000000b0 -32736\(gp\) 00000070 00000070 FUNC    UND c2
+  000000b4 -32732\(gp\) 00000000 00000000 OBJECT  ABS g1
diff --git a/ld/testsuite/ld-mips-elf/gotpc-o32.s b/ld/testsuite/ld-mips-elf/gotpc-o32.s
new file mode 100644
index 00000000000..3ac3b650aa1
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc-o32.s
@@ -0,0 +1,45 @@
+	.text
+	.globl	f1
+	.set	nomips16
+	.set	nomicromips
+	.ent	f1
+	.type	f1, @function
+f1:
+	.set	noreorder
+	.set	nomacro
+	addiu	$sp,$sp,-32
+	sw	$31,28($sp)
+	sw	$16,24($sp)
+	li	$16,0
+
+	bal	. + 8
+	lui	$25,%gotpc_call_hi(c1)
+	addu	$25,$25,$31
+	lw	$25,%gotpc_call_lo(c1)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+	aluipc	$25,%gotpc_call_ahi(c2)
+	lw	$25,%gotpc_call_alo(c2)($25)
+	jalr	$25
+	nop
+	addu	$16,$16,$2
+
+	bal	. + 8
+	lui	$2,%gotpc_hi(g1)
+	addu	$2,$2,$31
+	lw	$2,%gotpc_lo(g1)($2)
+	addu	$2,$2,$16
+	aluipc	$3,%gotpc_ahi(g2)
+	lw	$3,%gotpc_alo(g2)($3)
+	addu	$2,$2,$3
+
+	lw	$16,24($sp)
+	lw	$31,28($sp)
+	jr	$31
+	addiu	$sp,$sp,32
+
+	.set	macro
+	.set	reorder
+	.end	f1
+	.size	f1, .-f1
diff --git a/ld/testsuite/ld-mips-elf/gotpc.ld b/ld/testsuite/ld-mips-elf/gotpc.ld
new file mode 100644
index 00000000000..3dadc7e6d4d
--- /dev/null
+++ b/ld/testsuite/ld-mips-elf/gotpc.ld
@@ -0,0 +1,17 @@
+ENTRY (foo);
+SECTIONS
+{
+  .text : { *(.text) }
+  .MIPS.stubs : { *(.MIPS.stubs) }
+  HIDDEN (_gp = ALIGN(16) + 0x7ff0);
+  .got : { *(.got) }
+  .dynamic : { *(.dynamic) }
+  .hash : { *(.hash) }
+  .dynsym : { *(.dynsym) }
+  .dynstr : { *(.dynstr) }
+  .pdr : { *(.pdr) }
+  .shstrtab : { *(.shstrtab) }
+  .symtab : { *(.symtab) }
+  .strtab : { *(.strtab) }
+  /DISCARD/ : { *(*) }
+}
diff --git a/ld/testsuite/ld-mips-elf/mips-elf.exp b/ld/testsuite/ld-mips-elf/mips-elf.exp
index 50af78d1430..550f373efdc 100644
--- a/ld/testsuite/ld-mips-elf/mips-elf.exp
+++ b/ld/testsuite/ld-mips-elf/mips-elf.exp
@@ -719,6 +719,29 @@ if { $linux_gnu } {
 		    "readelf --relocs gp-hidden${suff64}.rd" \
 		    "readelf --syms gp-hidden.sd"] \
 		"gp-hidden-${abi}"]]
+
+	set gotpc_asflags [regsub -- {-march=[^[:blank:]]*} $abi_asflags($abi) {}]
+	run_ld_link_tests [list \
+	    [list \
+		"MIPS R_MIPS_GOTPC support (Prepare, $abi)" \
+		"$abi_ldflags($abi) -shared -T gotpc.ld" \
+		"" \
+		"$gotpc_asflags -mips64r6 -KPIC" \
+		[list gotpc-callee.s] \
+		"" \
+		"libgotpc-callee-${abi}.so"]]
+	run_ld_link_tests [list \
+	    [list \
+		"MIPS R_MIPS_GOTPC support ($abi)" \
+		"$abi_ldflags($abi) -shared -T gotpc.ld \
+		-Ltmpdir -lgotpc-callee-${abi}" \
+		"" \
+		"$gotpc_asflags -mips64r6 -KPIC" \
+		[list gotpc-${abi}.s] \
+		[list \
+		    "objdump -DTC gotpc-${abi}.dd" \
+		    "readelf -A gotpc-${abi}.gd"] \
+		"gotpc-${abi}.so"]]
     }
 }
 
-- 
2.39.2


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

end of thread, other threads:[~2024-04-18 10:56 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-02 12:51 [PATCH v4] MIPS: Support PCREL GOT access YunQiang Su
2024-02-02 13:30 ` Maciej W. Rozycki
2024-02-03  2:03   ` YunQiang Su
2024-03-16  8:51 YunQiang Su
2024-03-26  2:18 ` YunQiang Su
2024-04-03  4:04 ` Fangrui Song
     [not found] ` <DS7PR12MB57650A5401095ED70D3AB0EDCB3D2@DS7PR12MB5765.namprd12.prod.outlook.com>
2024-04-18 10:56   ` YunQiang Su

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).