You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保RISC-V自定义PAC/AUT指令编译后成对出现?

问题描述

我正在修改RISC-V ISA,为返回地址添加认证(pac指令)与校验(aut指令)逻辑,要求函数调用和返回时两者成对出现。使用RISC-V GNU工具链编译程序时,发现aut指令数量仅为pac的一半,导致因未认证RA超出范围引发取指段错误。我已在riscv_expand_prologue和riscv_expand_epilogue函数中分别添加了pac和aut相关指令代码,但问题仍存在,请问如何让这两类指令在函数首尾正确成对生成?

附加已添加的编译器代码片段:

void
riscv_expand_prologue (int style)
{

...
//veriable defination and initialization

if (cfun->machine->naked_p)
{
gcc_assert (style == NORMAL_RETURN);

      emit_jump_insn (gen_return ());
    
      return;
    }

+ //load key
+ insn = emit(gen_lui(gen_rtx_REG(Pmode,T3_REGNUM),
+ GEN_INT(32)));
+ insn = emit(gen_slli(gen_rtx_REG(Pmode,T3_REGNUM),
+ gen_rtx_REG(Pmode,T3_REGNUM),
+ GEN_INT(32)));
+ //inserting pac into ra
+ insn = emit(gen_pac(gen_rtx_REG(Pmode,RETURN_ADDR_REGNUM),
+                      gen_rtx_REG(Pmode,RETURN_ADDR_REGNUM),
+                      gen_rtx_REG(Pmode,T3_REGNUM)));
+ add_reg_note (insn, REG_CFA_TOGGLE_RA_MANGLE, const0_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;

...

}

void
riscv_expand_epilogue (int style)
{

...
//veriable defination and initialization

if (cfun->machine->naked_p)
{
gcc_assert (style == NORMAL_RETURN);

      emit_jump_insn (gen_return ());
    
      return;

}

+ /* pac */
+ //load key
+ insn = emit(gen_lui(gen_rtx_REG(Pmode,T3_REGNUM),
+ GEN_INT(32)));
+ insn = emit(gen_slli(gen_rtx_REG(Pmode,T3_REGNUM),
+ gen_rtx_REG(Pmode,T3_REGNUM),
+ GEN_INT(32)));
+ //authenticate ra and strip pac from ra
+ insn = emit(gen_aut(gen_rtx_REG(Pmode,RETURN_ADDR_REGNUM),
+                      gen_rtx_REG(Pmode,RETURN_ADDR_REGNUM),
+                      gen_rtx_REG(Pmode,T3_REGNUM)));
+ add_reg_note (insn, REG_CFA_TOGGLE_RA_MANGLE, const0_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;

...

}
解决方案

以下是几个针对性的排查和修复方向:

  • 处理非标准返回路径
    仅在riscv_expand_epilogue中添加aut只能覆盖函数末尾的标准返回,但很多函数存在提前return、尾调用或异常跳转等路径,这些路径不会触发epilogue逻辑,导致遗漏aut。

    • 对于提前return:在GCC的lower阶段或指令生成阶段,识别所有直接返回的指令,在跳转前插入aut校验逻辑;
    • 对于尾调用:尾调用会直接复用RA寄存器跳转,不需要保存和恢复RA,因此要在prologue阶段判断是否为尾调用函数,避免生成pac,或者如果已生成pac,必须先执行aut再进行尾调用跳转。
  • 覆盖特殊函数属性场景
    除了naked函数,部分函数可能带有__attribute__((no_prologue))或__attribute__((no_epilogue))属性,导致你的pac/aut代码被跳过。需要在代码中增加对这些属性的判断,确保符合条件的函数仍能生成配对指令。

  • 防止优化阶段误删指令
    GCC的优化模块可能会将未标记为必要的aut指令误判为冗余代码删除。要确保aut指令的寄存器注释和框架标记正确:

    • 确认REG_CFA_TOGGLE_RA_MANGLE注释被调试信息和优化器正确识别,标记RA寄存器的状态变化;
    • 可以给aut指令添加REG_NO_DESTROY标记,避免被寄存器分配或优化阶段误处理。
  • 修正密钥加载逻辑
    当前代码在prologue和epilogue中重复加载密钥,且32位RISC-V系统下slli 32位会导致寄存器清零(密钥变成0),这会让pac/aut失去作用甚至引发错误。建议:

    • 统一密钥加载逻辑,比如将密钥存入全局内存或固定寄存器,避免重复操作;
    • 根据目标架构(32/64位)调整密钥生成代码,确保密钥值符合预期。
  • 通过RTL dump验证指令生成
    使用-fdump-rtl-all参数编译目标程序,查看每个函数的RTL中间代码输出,对比哪些函数只生成了pac没有aut,分析这些函数的结构特征(比如多返回路径、尾调用、特殊属性),针对性修复遗漏场景。

内容的提问来源于stack exchange,提问作者Yuhe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 14:50:29