diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 040d5560cdf..e88e5966c22 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -7460,6 +7460,7 @@ enum bfd_reloc_code_real
   BFD_RELOC_LARCH_ADD_ULEB128,
   BFD_RELOC_LARCH_SUB_ULEB128,
   BFD_RELOC_LARCH_64_PCREL,
+  BFD_RELOC_LARCH_ALIGN_MAX,
   BFD_RELOC_UNUSED
 };
 typedef enum bfd_reloc_code_real bfd_reloc_code_real_type;
diff --git a/bfd/elfnn-loongarch.c b/bfd/elfnn-loongarch.c
index 024c5d4cd96..7a9b96018ec 100644
--- a/bfd/elfnn-loongarch.c
+++ b/bfd/elfnn-loongarch.c
@@ -3534,6 +3534,7 @@ loongarch_elf_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
 
 	case R_LARCH_RELAX:
 	case R_LARCH_ALIGN:
+	case R_LARCH_ALIGN_MAX:
 	  r = bfd_reloc_continue;
 	  unresolved_reloc = false;
 	  break;
@@ -3891,6 +3892,63 @@ loongarch_relax_align (bfd *abfd, asection *sec,
 				   rel->r_addend - nop_bytes, link_info);
 }
 
+static bool
+loongarch_relax_align_max (bfd *abfd, asection *sec,
+			asection *sym_sec,
+			struct bfd_link_info *link_info,
+			Elf_Internal_Rela *rel,
+			bfd_vma symval)
+{
+  bfd_byte *contents = elf_section_data (sec)->this_hdr.contents;
+
+  bfd_vma alignment = 1, pos, max;
+
+  /* 2^n = 2<<(n-1).  */
+  alignment = 2 << ((rel->r_addend & 0xff) - 1);
+  max = (rel->r_addend >> 8);
+
+  symval -= (alignment - 4);
+  bfd_vma aligned_addr = ((symval - 1) & ~(alignment - 1)) + alignment;
+  bfd_vma nop_bytes = aligned_addr - symval;
+
+  /* Once we've handled an R_LARCH_ALIGN, we can't relax anything else.  */
+  sec->sec_flg0 = true;
+
+  /* Make sure there are enough NOPs to actually achieve the alignment.  */
+  if ((alignment - 4) < nop_bytes)
+    {
+      _bfd_error_handler
+	(_("%pB(%pA+%#" PRIx64 "): %" PRId64 " bytes required for alignment "
+	   "to %" PRId64 "-byte boundary, but only %" PRId64 " present"),
+	 abfd, sym_sec, (uint64_t) rel->r_offset,
+	 (int64_t) nop_bytes, (int64_t) alignment, (int64_t) rel->r_addend);
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
+
+  /* Change R_LARCH_ALIGN_MAX to R_LARCH_NONE.  */
+  rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
+
+  /* If doing the alignemnt would require skipping more bytes than the
+     specified maximum, then the alignment is not done at all (delete all NOPs).
+     If the maximum is 0, the alignment can skip any bytes.  */
+  if (max > 0 && nop_bytes > max)
+    return loongarch_relax_delete_bytes (abfd, sec, rel->r_offset,
+					 alignment - 4, link_info);
+
+  /* If the number of NOPs is already correct, there's nothing to do.  */
+  if (nop_bytes == (alignment - 4))
+    return true;
+
+  /* Write as many LOONGARCH NOPs as we need.  */
+  for (pos = 0; pos < (nop_bytes & -4); pos += 4)
+    bfd_putl32 (LARCH_NOP, contents + rel->r_offset + pos);
+
+  /* Delete the excess NOPs.  */
+  return loongarch_relax_delete_bytes (abfd, sec, rel->r_offset + nop_bytes,
+				       (alignment - 4) - nop_bytes, link_info);
+}
+
 static bool
 loongarch_elf_relax_section (bfd *abfd, asection *sec,
 			       struct bfd_link_info *info,
@@ -4005,6 +4063,14 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	   if (symtype != STT_SECTION)
 	     symval += rel->r_addend;
 	}
+      /* For R_LARCH_ALIGN_MAX and R_LARCH_ALIGN, symval is sec_addr(sym_sec)
+	 + rel->r_offset + (alingmeng - 4).
+	 For R_LARCH_ALIGN, alignmeng-4 is r_addend.
+	 For R_LARCH_ALIGN_MAX, alignment-4 is 2^(r_addend & 0xff)-4.  */
+      else if (R_LARCH_ALIGN_MAX == ELFNN_R_TYPE (rel->r_info))
+	symval += ((2 << ((rel->r_addend & 0xff) - 1)) - 4);
+      else if (R_LARCH_ALIGN == ELFNN_R_TYPE (rel->r_info))
+	symval += rel->r_addend;
       else
 	symval += rel->r_addend;
 
@@ -4016,6 +4082,12 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	  if (1 == info->relax_pass)
 	    loongarch_relax_align (abfd, sec, sym_sec, info, rel, symval);
 	  break;
+
+	case R_LARCH_ALIGN_MAX:
+	  if (1 == info->relax_pass)
+	    loongarch_relax_align_max (abfd, sec, sym_sec, info, rel, symval);
+	  break;
+
 	case R_LARCH_DELETE:
 	  if (1 == info->relax_pass)
 	    {
@@ -4023,11 +4095,13 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 	      rel->r_info = ELFNN_R_INFO (0, R_LARCH_NONE);
 	    }
 	  break;
+
 	case R_LARCH_PCALA_HI20:
 	  if (0 == info->relax_pass && (i + 4) <= sec->reloc_count)
 	    loongarch_relax_pcala_addi (abfd, sec, sym_sec, rel, symval,
 					info, again);
 	  break;
+
 	case R_LARCH_GOT_PC_HI20:
 	  if (local_got && 0 == info->relax_pass
 	      && (i + 4) <= sec->reloc_count)
@@ -4037,6 +4111,7 @@ loongarch_elf_relax_section (bfd *abfd, asection *sec,
 					    info, again);
 	    }
 	  break;
+
 	default:
 	  break;
 	}
diff --git a/bfd/elfxx-loongarch.c b/bfd/elfxx-loongarch.c
index a970a257aa9..072dbc7494f 100644
--- a/bfd/elfxx-loongarch.c
+++ b/bfd/elfxx-loongarch.c
@@ -1547,6 +1547,25 @@ static loongarch_reloc_howto_type loongarch_howto_table[] =
 	 NULL,					/* adjust_reloc_bits */
 	 NULL),					/* larch_reloc_type_name */
 
+  /* The lowest 8 bits is the first expression of .align,
+     other bits is the third expression of .align.  */
+  LOONGARCH_HOWTO (R_LARCH_ALIGN_MAX,		/* type (110).  */
+	 0,					/* rightshift.  */
+	 0,					/* size.  */
+	 0,					/* bitsize.  */
+	 false,					/* pc_relative.  */
+	 0,					/* bitpos.  */
+	 complain_overflow_dont,		/* complain_on_overflow.  */
+	 bfd_elf_generic_reloc,			/* special_function.  */
+	 "R_LARCH_ALIGN_MAX",			/* name.  */
+	 false,					/* partial_inplace.  */
+	 0,					/* src_mask.  */
+	 0,					/* dst_mask.  */
+	 false,					/* pcrel_offset.  */
+	 BFD_RELOC_LARCH_ALIGN_MAX,		/* bfd_reloc_code_real_type.  */
+	 NULL,					/* adjust_reloc_bits.  */
+	 NULL),					/* larch_reloc_type_name.  */
+
 };
 
 reloc_howto_type *
diff --git a/bfd/libbfd.h b/bfd/libbfd.h
index cc432677a81..79172590b19 100644
--- a/bfd/libbfd.h
+++ b/bfd/libbfd.h
@@ -3599,6 +3599,7 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@",
   "BFD_RELOC_LARCH_ADD_ULEB128",
   "BFD_RELOC_LARCH_SUB_ULEB128",
   "BFD_RELOC_LARCH_64_PCREL",
+  "BFD_RELOC_LARCH_ALIGN_MAX",
  "@@overflow: BFD_RELOC_UNUSED@@",
 };
 #endif
diff --git a/bfd/reloc.c b/bfd/reloc.c
index 93ebad879e0..3353cbb2c2b 100644
--- a/bfd/reloc.c
+++ b/bfd/reloc.c
@@ -8292,6 +8292,9 @@ ENUMX
 ENUMX
   BFD_RELOC_LARCH_64_PCREL
 
+ENUMX
+  BFD_RELOC_LARCH_ALIGN_MAX
+
 ENUMDOC
   LARCH relocations.
 
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index d1ce111c186..8c60dd555de 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -1646,7 +1646,7 @@ loongarch_make_nops (char *buf, bfd_vma bytes)
    the correct alignment now because of other linker relaxations.  */
 
 bool
-loongarch_frag_align_code (int n)
+loongarch_frag_align_code (int n, int max)
 {
   bfd_vma bytes = (bfd_vma) 1 << n;
   bfd_vma insn_alignment = 4;
@@ -1666,12 +1666,12 @@ loongarch_frag_align_code (int n)
   nops = frag_more (worst_case_bytes);
 
   ex.X_op = O_constant;
-  ex.X_add_number = worst_case_bytes;
+  ex.X_add_number = ((max << 8) | n);
 
   loongarch_make_nops (nops, worst_case_bytes);
 
   fix_new_exp (frag_now, nops - frag_now->fr_literal, 0,
-	       &ex, false, BFD_RELOC_LARCH_ALIGN);
+	       &ex, false, BFD_RELOC_LARCH_ALIGN_MAX);
 
   /* We need to start a new frag after the alignment which may be removed by
      the linker, to prevent the assembler from computing static offsets.
diff --git a/gas/config/tc-loongarch.h b/gas/config/tc-loongarch.h
index 4afa38422d6..194ee107c0a 100644
--- a/gas/config/tc-loongarch.h
+++ b/gas/config/tc-loongarch.h
@@ -49,11 +49,11 @@ extern int loongarch_relax_frag (asection *, struct frag *, long);
 #define md_undefined_symbol(name) (0)
 #define md_operand(x)
 
-extern bool loongarch_frag_align_code (int);
+extern bool loongarch_frag_align_code (int, int);
 #define md_do_align(N, FILL, LEN, MAX, LABEL)				\
   if ((N) != 0 && !(FILL) && !need_pass_2 && subseg_text_p (now_seg))	\
     {									\
-      if (loongarch_frag_align_code (N))				\
+      if (loongarch_frag_align_code (N, MAX))				\
 	goto LABEL;							\
     }
 
diff --git a/gas/testsuite/gas/loongarch/relax_align.d b/gas/testsuite/gas/loongarch/relax_align.d
index 1810eb4cae7..cc2562fc25c 100644
--- a/gas/testsuite/gas/loongarch/relax_align.d
+++ b/gas/testsuite/gas/loongarch/relax_align.d
@@ -7,20 +7,20 @@
 
 Disassembly of section .text:
 
-00000000.* <L1>:
-[ 	]+0:[ 	]+1a000004[ 	]+pcalau12i[ 	]+\$a0,[ 	]+0
-[ 	]+0:[ 	]+R_LARCH_PCALA_HI20[ 	]+L1
-[ 	]+0:[ 	]+R_LARCH_RELAX[ 	]+\*ABS\*
-[ 	]+4:[ 	]+02c00084[ 	]+addi\.d[ 	]+\$a0,[ 	]+\$a0,[ 	]+0
-[ 	]+4:[ 	]+R_LARCH_PCALA_LO12[ 	]+L1
-[ 	]+4:[ 	]+R_LARCH_RELAX[ 	]+\*ABS\*
-[ 	]+8:[ 	]+03400000[ 	]+nop[ 	]+
-[ 	]+8:[ 	]+R_LARCH_ALIGN[ 	]+\*ABS\*\+0xc
-[ 	]+c:[ 	]+03400000[ 	]+nop[ 	]+
-[ 	]+10:[ 	]+03400000[ 	]+nop[ 	]+
-[ 	]+14:[ 	]+1a000004[ 	]+pcalau12i[ 	]+\$a0,[ 	]+0
-[ 	]+14:[ 	]+R_LARCH_PCALA_HI20[ 	]+L1
-[ 	]+14:[ 	]+R_LARCH_RELAX[ 	]+\*ABS\*
-[ 	]+18:[ 	]+02c00084[ 	]+addi\.d[ 	]+\$a0,[ 	]+\$a0,[ 	]+0
-[ 	]+18:[ 	]+R_LARCH_PCALA_LO12[ 	]+L1
-[ 	]+18:[ 	]+R_LARCH_RELAX[ 	]+\*ABS\*
+.* <.text>:
+[ 	]+0:[ 	]+1a000004[ 	]+pcalau12i[ 	]+\$a0, 0
+[ 	]+0: R_LARCH_PCALA_HI20[ 	]+L1
+[ 	]+0: R_LARCH_RELAX[ 	]+\*ABS\*
+[ 	]+4:[ 	]+02c00084[ 	]+addi.d[ 	]+\$a0, \$a0, 0
+[ 	]+4: R_LARCH_PCALA_LO12[ 	]+L1
+[ 	]+4: R_LARCH_RELAX[ 	]+\*ABS\*
+[ 	]+8:[ 	]+03400000[ 	]+nop.*
+[ 	]+8: R_LARCH_ALIGN_MAX[ 	]+\*ABS\*\+0x4
+[ 	]+c:[ 	]+03400000[ 	]+nop.*
+[ 	]+10:[ 	]+03400000[ 	]+nop.*
+[ 	]+14:[ 	]+1a000004[ 	]+pcalau12i[ 	]+\$a0, 0
+[ 	]+14: R_LARCH_PCALA_HI20[ 	]+L1
+[ 	]+14: R_LARCH_RELAX[ 	]+\*ABS\*
+[ 	]+18:[ 	]+02c00084[ 	]+addi.d[ 	]+\$a0, \$a0, 0
+[ 	]+18: R_LARCH_PCALA_LO12[ 	]+L1
+[ 	]+18: R_LARCH_RELAX[ 	]+\*ABS\*
diff --git a/gas/testsuite/gas/loongarch/relax_align.s b/gas/testsuite/gas/loongarch/relax_align.s
index 3880d783e79..482600e43a5 100644
--- a/gas/testsuite/gas/loongarch/relax_align.s
+++ b/gas/testsuite/gas/loongarch/relax_align.s
@@ -1,5 +1,4 @@
   .text
-L1:
   la.local $a0, L1
   .align 4
   la.local $a0, L1
diff --git a/include/elf/loongarch.h b/include/elf/loongarch.h
index e31395e13d5..cce43bb4a10 100644
--- a/include/elf/loongarch.h
+++ b/include/elf/loongarch.h
@@ -251,6 +251,8 @@ RELOC_NUMBER (R_LARCH_SUB_ULEB128, 108)
 
 RELOC_NUMBER (R_LARCH_64_PCREL, 109)
 
+RELOC_NUMBER (R_LARCH_ALIGN_MAX, 110)
+
 END_RELOC_NUMBERS (R_LARCH_count)
 
 /* Processor specific flags for the ELF header e_flags field.  */
diff --git a/ld/testsuite/ld-loongarch-elf/relax-align-max.dd b/ld/testsuite/ld-loongarch-elf/relax-align-max.dd
new file mode 100644
index 00000000000..0dd739ffc4a
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/relax-align-max.dd
@@ -0,0 +1,4 @@
+#...
+.*pcaddi.*
+.*pcaddi.*
+#pass
diff --git a/ld/testsuite/ld-loongarch-elf/relax-align-max.s b/ld/testsuite/ld-loongarch-elf/relax-align-max.s
new file mode 100644
index 00000000000..2e829392fd6
--- /dev/null
+++ b/ld/testsuite/ld-loongarch-elf/relax-align-max.s
@@ -0,0 +1,6 @@
+  .text
+.L1:
+  la.local $t0, .L1
+  # If skipping more bytes than 4, then the alignment is not done at all.
+  .align 4, , 4
+  la.local $t0, .L1
diff --git a/ld/testsuite/ld-loongarch-elf/relax.exp b/ld/testsuite/ld-loongarch-elf/relax.exp
index 24d79ed5c20..a4bfd0fe0c7 100644
--- a/ld/testsuite/ld-loongarch-elf/relax.exp
+++ b/ld/testsuite/ld-loongarch-elf/relax.exp
@@ -130,6 +130,20 @@ if [istarget loongarch64-*-*] {
 	  ] \
       ]
 
+  run_ld_link_tests \
+      [list \
+	  [list \
+	      "loongarch relax-align-max" \
+	      "-e 0x0 -z relro" "" \
+	      "--no-warn" \
+	      {relax-align-max.s} \
+	      [list \
+		  [list objdump -d relax-align-max.dd] \
+	      ] \
+	      "relax-align-max" \
+	  ] \
+      ]
+
   set objdump_flags "-s -j .data"
   run_ld_link_tests \
       [list \
