[binutils-gdb] RISC-V: Check for conflicting extensions when the linker merges arch attributes

Nelson Chu via Binutils-cvs <[email protected]> Thu, 9 Jul 2026 01:08:22 +0000 (GMT)
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a270b84fca1e9705655317c8b9428d3411f7a7f1

commit a270b84fca1e9705655317c8b9428d3411f7a7f1
Author: Ethan Y. C. Liang <[email protected]>
Date:   Mon Jul 6 19:44:28 2026 +0800

    RISC-V: Check for conflicting extensions when the linker merges arch attributes
    
    The linker validates the arch of each input when parsing it, but never
    checks the merged arch as a whole, so conflicting extensions could
    silently slip through.  The merge can even imply extensions that
    neither input implies alone, e.g. `c' from one input and `d' from
    another imply `zcd', which conflicts with `zcmp'.
    
    After merging the input archs, add the implicit extensions to the
    merged arch, then run the conflict checks on it and reject the merge
    if any conflict is found.  Add tests for such merges.

Diff:
---
 bfd/elfxx-riscv.c                                      | 8 ++++++++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.d  | 5 +++++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.l  | 2 ++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01a.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01b.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.d  | 5 +++++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.l  | 2 ++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02a.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02b.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.d  | 5 +++++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.l  | 2 ++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03a.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03b.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.d  | 5 +++++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.l  | 2 ++
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04a.s | 1 +
 ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04b.s | 1 +
 ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp             | 4 ++++
 18 files changed, 48 insertions(+)

diff --git a/bfd/elfxx-riscv.c b/bfd/elfxx-riscv.c
index 8fc4e1408f9..c3cb4197ba8 100644
--- a/bfd/elfxx-riscv.c
+++ b/bfd/elfxx-riscv.c
@@ -3814,6 +3814,14 @@ riscv_merge_arch_attr_info (bfd *ibfd, char *in_arch, char *out_arch,
       goto cleanup;
     }
 
+  /* Add the implicit subsets implied by the merged subset list, then
+     check if the result is conflicting.  */
+  riscv_parse_subset_t riscv_rps_ld_merged =
+    {&merged_subsets, _bfd_error_handler, &xlen_in, NULL, false};
+  riscv_parse_add_implicit_subsets (&riscv_rps_ld_merged);
+  if (!riscv_parse_check_conflicts (&riscv_rps_ld_merged))
+    goto cleanup;
+
   /* Free the previous merged_arch_str which called xmalloc.  */
   free (merged_arch_str);
 
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.d b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.d
new file mode 100644
index 00000000000..f99677cb767
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.d
@@ -0,0 +1,5 @@
+#source: attr-merge-arch-failed-01a.s
+#source: attr-merge-arch-failed-01b.s
+#as: -mabi=ilp32
+#ld: -r -m[riscv_choose_ilp32_emul]
+#error_output: attr-merge-arch-failed-01.l
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.l b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.l
new file mode 100644
index 00000000000..ad6d2630211
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01.l
@@ -0,0 +1,2 @@
+.*: `zfinx' conflicts with the `f/d/q/zfh/zfhmin' extension
+.*: failed to merge target specific data of file .*
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01a.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01a.s
new file mode 100644
index 00000000000..8f2a98f95f3
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01a.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32i_zfinx"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01b.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01b.s
new file mode 100644
index 00000000000..a44b2a0d346
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-01b.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32if"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.d b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.d
new file mode 100644
index 00000000000..4fb1980c97d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.d
@@ -0,0 +1,5 @@
+#source: attr-merge-arch-failed-02a.s
+#source: attr-merge-arch-failed-02b.s
+#as: -mabi=ilp32
+#ld: -r -m[riscv_choose_ilp32_emul]
+#error_output: attr-merge-arch-failed-02.l
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.l b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.l
new file mode 100644
index 00000000000..930e03123e1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02.l
@@ -0,0 +1,2 @@
+.*: zcmp' is incompatible with `d' and `c', or `zcd' extension
+.*: failed to merge target specific data of file .*
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02a.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02a.s
new file mode 100644
index 00000000000..3f79059d5d0
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02a.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32i_zcmp"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02b.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02b.s
new file mode 100644
index 00000000000..95bfe1f6d9f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-02b.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32i_zcd"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.d b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.d
new file mode 100644
index 00000000000..def49c3fb3f
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.d
@@ -0,0 +1,5 @@
+#source: attr-merge-arch-failed-03a.s
+#source: attr-merge-arch-failed-03b.s
+#as: -mabi=ilp32
+#ld: -r -m[riscv_choose_ilp32_emul]
+#error_output: attr-merge-arch-failed-03.l
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.l b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.l
new file mode 100644
index 00000000000..d9075d0c30e
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03.l
@@ -0,0 +1,2 @@
+.*: `zclsd' conflicts with the `c\+f'/`zcf' extension
+.*: failed to merge target specific data of file .*
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03a.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03a.s
new file mode 100644
index 00000000000..f7f9ceb11f6
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03a.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32i_zclsd"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03b.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03b.s
new file mode 100644
index 00000000000..f18129dc55d
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-03b.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32i_zcf"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.d b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.d
new file mode 100644
index 00000000000..5445936df44
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.d
@@ -0,0 +1,5 @@
+#source: attr-merge-arch-failed-04a.s
+#source: attr-merge-arch-failed-04b.s
+#as: -mabi=ilp32
+#ld: -r -m[riscv_choose_ilp32_emul]
+#error_output: attr-merge-arch-failed-04.l
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.l b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.l
new file mode 100644
index 00000000000..930e03123e1
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04.l
@@ -0,0 +1,2 @@
+.*: zcmp' is incompatible with `d' and `c', or `zcd' extension
+.*: failed to merge target specific data of file .*
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04a.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04a.s
new file mode 100644
index 00000000000..e79c25b7828
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04a.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32ic"
diff --git a/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04b.s b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04b.s
new file mode 100644
index 00000000000..c7468e4e072
--- /dev/null
+++ b/ld/testsuite/ld-riscv-elf/attr-merge-arch-failed-04b.s
@@ -0,0 +1 @@
+	.attribute arch, "rv32id_zcmp"
diff --git a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
index 8e26ccff10a..57f3139c9f6 100644
--- a/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
+++ b/ld/testsuite/ld-riscv-elf/ld-riscv-elf.exp
@@ -157,6 +157,10 @@ if [istarget "riscv*-*-*"] {
     run_dump_test "attr-merge-arch-01"
     run_dump_test "attr-merge-arch-02"
     run_dump_test "attr-merge-arch-03"
+    run_dump_test "attr-merge-arch-failed-01"
+    run_dump_test "attr-merge-arch-failed-02"
+    run_dump_test "attr-merge-arch-failed-03"
+    run_dump_test "attr-merge-arch-failed-04"
     run_dump_test "attr-merge-strict-align-01"
     run_dump_test "attr-merge-strict-align-02"
     run_dump_test "attr-merge-strict-align-03"