【问题标题】:What do %pcrel_hi and %pcrel_lo actually do?%pcrel_hi 和 %pcrel_lo 实际上是做什么的?
【发布时间】:2021-01-25 04:32:17
【问题描述】:

riscv-asm-manual控制和状态寄存器部分,有一个例子:

.equ RTC_BASE,      0x40000000
.equ TIMER_BASE,    0x40004000

# setup machine trap vector
1:      auipc   t0, %pcrel_hi(mtvec)        # load mtvec(hi)
        addi    t0, t0, %pcrel_lo(1b)       # load mtvec(lo)
        csrrw   zero, mtvec, t0

...
# break on interrupt
mtvec:
        csrrc  t0, mcause, zero
        bgez t0, fail       # interrupt causes are less than zero
        slli t0, t0, 1      # shift off high bit
...

我猜%pcrel_hi(mtvec) 计算mtvec 和当前PC 之间的高距离(这里是1 符号的地址)。假设1符号的地址为0x80010000,mtvec的地址为0x80020040。那么%pcrel_hi(mtvec) = (0x80020040 - 0x80010000) >> 12 = 0x00010,所以auipc的结果是0x00010 << 12 + PC = 0x00010000 + 0x80010000 = 0x80020000

%pcrel_lo1b 作为其参数。如何计算其结果并得到mtvec的最终地址? addi t0, t0, %pcrel_lo(mtvect) 似乎是直观的代码,但实际上并非如此。为什么?

【问题讨论】:

  • godbolt.org/z/EebPsc 确认返回全局变量地址的普通 C 函数的普通编译器输出做同样的事情,使用 addi%pcrel_lo(.LBB0_1) auipc 地址,而不是目标象征。所以这不是一些特殊的用例做一些不同的事情,这显然是 RISC-V 始终适用于与位置无关的代码。
  • 是的,这似乎是一个约定,但我只是想知道%pcrel_lo(label) 将如何计算。
  • 我也没摸透,等人来回答这个问题。我只是在检查这是否正常之后才发表评论以显示我的结果,或者以某种方式特定于这个关于中断处理程序的示例。

标签: assembly riscv


【解决方案1】:

正如 Peter Cordes 在他的评论、您的链接以及 this one 中指出的那样。对于 addi,使用标签而不是直接使用符号,因为 addi 必须包含 pc 和符号之间的相对地址的 12 个低有效位。但是 pc 必须与用于 pcrel_high 的那个相同,因为从 binutils 的角度来看,不能保证这两个指令将相互跟随(这可能会导致计算不同)。因此,为了提供正确的电脑,选择的解决方案是使用标签。

现在对于您示例中的 1b,它不是计算的结果。所有计算都在链接器级别完成,汇编器只负责生成必要的重定位信息。 1b 表示向后 标签1。数字标签用于本地引用。对本地标签的引用后缀为“f”表示前向引用或后缀为“b”表示后向引用(它也存在于您的链接中)。

如果你组装这个汇编文件,你会看到标签将根据你的汇编器版本更改为.L1XXX,然后如果你执行riscv-objdump -r,你会看到你有一个R_RISCV_PCREL_LO12_I到这个标签在addi对应的offset上。

基本上你会有类似的东西:

OFFSET           TYPE              VALUE 
0000000000000000 R_RISCV_PCREL_HI20  mtvec
0000000000000000 R_RISCV_RELAX     *ABS*
0000000000000004 R_RISCV_PCREL_LO12_I  .L1^B

在此示例中,偏移量 0 是 .L1^B1 的偏移量(已转换的标签 1)。所以链接器将使用这个重定位来计算将用于 auipc 的值。

那么对于 addi 指令的偏移量 4,链接器将找到 R_RISCV_PCREL_LO12_I 重定位。它将使用值 .L1^B1 从重定位 (R_RISCV_PCREL_HI20) 中获取与该值的偏移量相对应的 pc 和符号。那么它将取 .L1^B1 的 pc 和找到的符号 mtvec 的地址之间的相对地址的 LSB 12 位。

【讨论】:

  • 当然标签提供了一种获取正确 PC 的方法,但我还没有弄清楚如何获取 PC 和符号之间差异的 12 个低有效位。
  • 好的,终于回答了这个问题。所以%pcrel_lo(label)label 的指令中引用了pcrel_hi 重定位,这就是为什么它不像addi t0, t0, %pcrel_lo(mtvec - 1b)。我想知道是否是这种情况,因为addilw/sw 需要了解目标地址的低位,而不仅仅是auipc,但直到现在你的回答对此一无所知。
  • lsb 12 位是在符号地址(32 位)和 pc(也是 32 位)之间计算的(它是标签的偏移量)。链接器可以通过使用重定位的 r-info 部分和符号表来获取 mtvec 的完整 32 位地址。
  • 我明白了,但是%pcrel_lo(1b)1 间接引用%pcrel_hi(mtvec) 是完全不明显的。这是在任何地方都没有解释过的部分(直到您上次编辑),并且与 %hi(symbol)%lo(symbol) 的工作方式完全不同。
  • 确实我的第一个回答并没有很好地阐明这一点。
猜你喜欢
  • 2013-05-21
  • 2011-03-11
  • 2010-11-26
  • 2015-06-23
  • 2017-12-26
  • 2016-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多