V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
amiwrong123
V2EX  ›  程序员

RISCv 给函数传递参数的时候,必须用 PC rel 的方式吗

  •  
  •   amiwrong123 · 9 天前 · 655 次点击

    所谓的 pc rel 的方式,比如说哈,我要传递第一个参数,是通过 auipc+addi 的方式来设置 a0.

    我现在编译程序的时候,会遇到一个 trancated PCRELHI20 的链接错误。下面是一个,我的问题场景的简化场景:

    extern int array[];// 这个数组的地址很高,在 0xFFFF_F000;这个符号来自于另一个 c 文件
    
    //下面的函数,都是基于 0x0 的地址开始进行分配
    int test(int a) {
        return a*gType;
    }
    
    int main() {
        test((int)array); //这里放置第一个参数的时候,就会报错
    }
    

    就是说,为了给 test 函数传递 array 这个实参,汇编指令使用了 auipc+addi 的方式,而且当前 pc 和 array 这个符号的地址太远了,导致了会报错。

        __asm__ volatile (
            "lui   a0, %%hi(array)\n\t"
            "addi  a0, a0, %%lo(array)\n\t"
            : 
            : "r"(array)
        );
    

    我尝试写了一个内嵌汇编,来模拟放置 a0 (也就是第一个参数),这里我用的是 lui+addi ,而不是 auipc+addi 的方式。但还是报一样的错。

    我真的不理解了,这个问题到底怎么解

    9 条回复    2025-04-17 13:40:07 +08:00
    cheese
        1
    cheese  
       9 天前
    试试马斯克的 grok3 ?我帮你尝试问了下,输出的回答“看起来”非常正确,但是 v2 不能贴 ai 回答,你试试
    XimCN
        2
    XimCN  
       9 天前 via iPhone   ❤️ 2
    这和 pc relative 或直接加载无关,单纯是地址太高超出了 risc-v 两条指令的立即数部分拼接能寻址的地址范围(总共 4GB )。要么把这个数组的地址放在一个低地址变量里做一次指针 load ,要么多加几条指令,把地址高位拼接出来并 shift 到高位,再用现在的方案把低位拼进去,组成一个完整的高地址
    amiwrong123
        3
    amiwrong123  
    OP
       9 天前
    @XimCN #2
    要么把这个数组的地址放在一个低地址变量里做一次指针 load
    ------
    extern int array[];

    uintptr_t ptr_to_array = (uintptr_t)array; // 变量本身位于低地址
    test((int)ptr_to_array);

    请教下,你是这个意思吗?再多定义一个指针变量,这个指针变量 离当前 PC 不会有那么远,所以就不会有问题。

    我刚才实测了一下,同样会出问题的地方,加了指针变量后,就不会编译报错了,哈哈。
    amiwrong123
        4
    amiwrong123  
    OP
       9 天前
    @XimCN #2
    单纯是地址太高超出了 risc-v 两条指令的立即数部分拼接能寻址的地址范围(总共 4GB )
    ----
    我理解是这样的吧,auipc+addi ,只能定位 当前 pc 前后 2GB 的空间。因为 auipc 的高位是符号位。

    但我这个例子里,当前 PC 离 array 的距离,大于了 2GB ,所以超过了呗?
    amiwrong123
        5
    amiwrong123  
    OP
       9 天前
    @XimCN #2
    要么多加几条指令,把地址高位拼接出来并 shift 到高位
    -----
    我想了一下,是不是 用 li + 左移 组合,多用几次,应该就 可以拼接出来了吧
    XimCN
        6
    XimCN  
       9 天前
    > 我理解是这样的吧,auipc+addi ,只能定位 当前 pc 前后 2GB 的空间。因为 auipc 的高位是符号位。
    > 但我这个例子里,当前 PC 离 array 的距离,大于了 2GB ,所以超过了呗?

    > 我想了一下,是不是 用 li + 左移 组合,多用几次,应该就 可以拼接出来了吧
    对,auipc+addi/lui+addi 一次可以拼接 32 bit 数据,用两组这样的组合可以拼出 64 bit 完整地址
    Shaaaadow
        7
    Shaaaadow  
       8 天前
    这不是传参的问题,是拿全局变量地址的问题。

    可以了解一下 PIC 这个概念,也可以试试像 `-mcmodel=medany` 或者 `-fno-pic` 这样的配置
    XimCN
        8
    XimCN  
       8 天前 via iPhone
    如果要用编译选项解决,mcmodel=any 不够,需要 mcmodel=large
    amiwrong123
        9
    amiwrong123  
    OP
       7 天前
    @Shaaaadow #7 #7

    很奇怪, 我在编译每个 o 文件时,加了这种`-fno-pic 选项,还是不行。甚至感觉编译出来的反汇编都是一样的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1240 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 23:34 · PVG 07:34 · LAX 16:34 · JFK 19:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.