<div style="font-size: 14.6667px; font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei";" data-mail-from="wemail-pc"><div id="wemail_placeholder_space" style="font-size: 11pt; line-height: 1.43;"><div style="font-size: 11pt; line-height: 1.43;"><span style="font-size: 11pt; line-height: 1.43;">Hi RISC-V maintainers and contributors,<div style=""><br style=""></div><div style="">When testing ZCMT functionality, I found that .option norelax does not actually prevent ZCMT instruction generation. Here's the test case I wrote:</div><div style=""><br style=""></div><div style="">Assembly</div><div style="">.section .text</div><div style="">.globl _start</div><div style="">.type _start, %function</div><div style="">_start:</div><div style="">    .option push</div><div style="">    .option norelax</div><div style="">    .rept 70</div><div style="">    call local_func</div><div style="">    .endr</div><div style="">    .option pop</div><div style="">    .rept 70</div><div style="">    call global_func</div><div style="">    .endr</div><div style="">    li a7, 93        # sys_exit</div><div style="">    li a0, 0         # exit code</div><div style="">    ecall</div><div style="">.type local_func, %function</div><div style="">local_func:</div><div style="">    ret</div><div style="">.globl global_func</div><div style="">.type global_func, %function</div><div style="">global_func:</div><div style="">    ret</div><div style="">With the following linker script:</div><div style=""><br style=""></div><div style="">Text</div><div style="">ENTRY(_start)</div><div style="">SECTIONS</div><div style="">{</div><div style="">    .text 0x80000000 : {</div><div style="">        *(.text)</div><div style="">    }</div><div style="">    .riscv.jvt : {</div><div style="">        *(.riscv.jvt)</div><div style="">    }</div><div style="">    .data : {</div><div style="">        *(.data)</div><div style="">    }</div><div style="">}</div><div style="">Observation:</div><div style="">The .option push / .option norelax / .option pop block should prevent relaxation for the enclosed call instructions, but ZCMT transformations are still being applied.</div><div style=""><br style=""></div><div style="">Root Cause:</div><div style="">In _bfd_riscv_relax_section(), the ZCMT relaxation path does not check whether the relocation is paired with R_RISCV_RELAX. When .option norelax is used, the assembler does not emit R_RISCV_RELAX relocations alongside the call instructions, but the linker still applies ZCMT relaxation unconditionally.</div><div style=""><br style=""></div><div style="">Proposed Fix:</div><div style="">Skip ZCMT relaxation if the call relocation is not paired with R_RISCV_RELAX:</div><div style=""><br style=""></div><div style="">Diff</div><div style="">--- a/bfd/elfnn-riscv.c</div><div style="">+++ b/bfd/elfnn-riscv.c</div><div style="">@@ -6067,6 +6067,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,</div><div style="">                relax_func = _bfd_riscv_jvt_record;</div><div style="">             else</div><div style="">                continue;</div><div style="">+            /* Skip if not paired with R_RISCV_RELAX (i.e. .option norelax).  */</div><div style="">+          if (i == sec->reloc_count - 1</div><div style="">+           || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX</div><div style="">+         || rel->r_offset != (rel + 1)->r_offset)</div><div style="">+           continue;</div><div style="">+            i++;</div><div style="">              *again = true;</div><div style="">            }</div><div style="">          else if (info->relax_trip == JVT_PROFILING_DETERMINE)</div><div style="">@@ -6076,6 +6082,12 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec,</div><div style="">                relax_func = _bfd_riscv_jvt_mark;</div><div style="">              else</div><div style="">                continue;</div><div style="">+           /* Skip if not paired with R_RISCV_RELAX (i.e. .option norelax).  */</div><div style="">+               if (i == sec->reloc_count - 1</div><div style="">+                 || ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX</div><div style="">+                 || rel->r_offset != (rel + 1)->r_offset)</div><div style="">+                   continue;</div><div style="">+                   i++;</div><div style="">            }</div><div style="">        }</div><div style="">     else if (info->relax_pass == RELAX_PASS_SHORTEN_LUI_CALL_TRREL_PCREL)</div><div style="">Explanation of the check:</div><div style=""><br style=""></div><div style="">i == sec->reloc_count - 1 — ensure there's a next relocation to check</div><div style="">ELFNN_R_TYPE ((rel + 1)->r_info) != R_RISCV_RELAX — verify the next relocation is R_RISCV_RELAX</div><div style="">rel->r_offset != (rel + 1)->r_offset — ensure the relax relocation is at the same offset (paired)</div><div style="">Testing:</div><div style="">With this patch applied, the test case correctly preserves the call instructions inside the .option norelax block while still applying ZCMT relaxation to the unprotected calls outside the block.</div></span></div></div><div style="font-size: 11pt; line-height: 1.43;"><br style=""></div><div class="qqmail_sign" id="wemailsigcontent" signid="501" style=""><div style="font-size: 11pt; line-height: 1.43;"><br style=""></div><div style="font-size: 11pt; line-height: 1.43;"><br style=""></div></div><div></div></div>