了解用于函数调用的 auipc+jalr 序列

2023-11-27

我试图阅读RISC-V生成的程序集gcc我发现gcc创建序列auipc+jalr对于某些函数调用,我不明白它是如何工作的。这是一个简单的例子。考虑以下C源文件:

unsigned long id(unsigned long x) {
    return x;
}

unsigned long add_one(unsigned long x) {
    return id(x)+1;
}

我用它编译gcc -O2 -fno-inline -c test.c我得到以下汇编代码:

$ objdump -d test.o

test.o:     file format elf64-littleriscv


Disassembly of section .text:

0000000000000000 <id>:
   0:   00008067            ret

0000000000000004 <add_one>:
   4:   ff010113            addi    sp,sp,-16
   8:   00113423            sd      ra,8(sp)
   c:   00000317            auipc   t1,0x0
  10:   000300e7            jalr    t1
  14:   00813083            ld      ra,8(sp)
  18:   00150513            addi    a0,a0,1
  1c:   01010113            addi    sp,sp,16
  20:   00008067            ret

让我困惑的是偏移处的两条线0x0c and 0x10,这就是函数的位置id应该被称为。根据spec, auipc t1,0x0应该写PC + 0x0<<12(这等于PC) to t1进而jalr t1(它被扩展到jalr ra,t1,0) 跳转到存储的地址t1并将返回地址存储到ra。所以我们最终跳到auipc线(偏移0x0c),不是入口点id。这里发生了什么?


反汇编目标文件时,显示的地址信息auipc/jalr有点任意,因为无论如何它都会被链接器重新定位。

您可以看到,在转储重定位信息时(添加-r到您的 objdump 调用):

0000000000000000 <id>:
   0:   8082                    ret
0000000000000002 <add_one>:
   2:   1141                    addi    sp,sp,-16
   4:   e406                    sd  ra,8(sp)
   6:   00000097            auipc   ra,0x0
            6: R_RISCV_CALL id
            6: R_RISCV_RELAX    *ABS*
   a:   000080e7            jalr    ra # 6 <add_one+0x4>
   e:   60a2                    ld  ra,8(sp)
  10:   0505                    addi    a0,a0,1
  12:   0141                    addi    sp,sp,16
  14:   8082                    ret

这些重定位条目告诉链接器以轻松的方式重定位跳转指令(RISC-V 工具链的默认设置)。这意味着它可以替换auipc+jalr仅与一个配对jal指令 iff 到目标地址的距离足够短。这种替换是有利的,因为它节省了指令,即生成的程序更短。显然,它使重定位过程变得有点复杂,因为后续跳转指令的偏移量需要相应调整。

(这可以通过禁用-mno-relax海湾合作委员会旗帜。)

为什么汇编器不能直接发出finalauipc/jalr/jal不需要重新定位翻译单元本地符号的指令?毕竟,这些跳跃是与电脑相关的。

一般来说,它不能,因为只有一个翻译单元的本地视图 1) 到外部符号的宽松重定位可能会更改所有后续到内部符号的偏移量,2) 链接器甚至可能应用一些高级规则,例如其中内部符号被外部符号覆盖,因此它确实必须在链接器中重新定位。或者,另一个例子,链接器删除了一个符号。

如果您想查看重定位的地址/偏移量,您必须反汇编链接的二进制文件,例如:

000000000001015c <id>:
   1015c:   8082                    ret
000000000001015e <add_one>:
   1015e:   1141                    addi    sp,sp,-16
   10160:   e406                    sd  ra,8(sp)
   10162:   ffbff0ef            jal ra,1015c <id>
   10166:   60a2                    ld  ra,8(sp)
   10168:   0505                    addi    a0,a0,1
   1016a:   0141                    addi    sp,sp,16
   1016c:   8082                    ret

As expected, the linker relaxes auipc+jalr to just jal. Unfortunately, objdump doesn't display the raw jal offset - 1015c is the absolute address after adding the offset to 10162.1

您可以自行解码第二列的二进制指令来验证:

   0xffbff0ef
=  0b11111111101111111111000011101111 | split into the offset parts
=>   1 1111111101 1 11111111          | i.e. off[20], off[10:1], off[11], off[19:12]
                                      | merge them into off[20:1]
=> 0b11111111111111111101             | left-shift by 1
=> 0b111111111111111111010            | sign-extend
=> 0b11111111111111111111111111111010
=  -6
=> 0x10162 - 6
=  0x1015c

这与 objdump 输出匹配。


1 That means GNU binutils objdump doesn't display the raw jal offset. In contrast, llvm-objdump (LLVM 9 introduces official RISC-V support) does display the raw offset:

000000000001015e add_one:
   1015e: 41 11                         addi    sp, sp, -16
   10160: 06 e4                         sd  ra, 8(sp)
   10162: ef f0 bf ff                   jal -6
   10166: a2 60                         ld  ra, 8(sp)
   10168: 05 05                         addi    a0, a0, 1
   1016a: 41 01                         addi    sp, sp, 16
   1016c: 82 80                         ret

然而,与 GNU binutils objdump 相比,llvm-objdump不包括结果绝对地址作为注释。它也没有注释相应的符号。因此,一般来说,GNU binutils objdump 输出可以说更有用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

了解用于函数调用的 auipc+jalr 序列 的相关文章

  • 在 DLL 中,函数表的结构如何?

    我一直在研究不明确支持我的操作系统的设备库的实现 特别是 我有一个反汇编的 DLL 以及大量的支持源代码 现在 功能表 导出表是如何构造的 我的理解是 第一个结构 data部分是 RVA 表 接下来是通过索引链接到第一个地址表的字符串表 这
  • 如何查看JVM中JIT编译的代码?

    有什么方法可以查看 JVM 中 JIT 生成的本机代码吗 一般用法 正如其他答案所解释的 您可以使用以下 JVM 选项运行 XX UnlockDiagnosticVMOptions XX PrintAssembly 根据特定方法进行过滤 您
  • 英特尔® 事务同步扩展新指令 (TSX-NI) 与英特尔 TSX 有何不同?

    我在Intel的页面上找到了 https ark intel com products 97123 Intel Core i5 7500 Processor 6M Cache up to 3 80 GHz https ark intel c
  • 推送 64 位 intel osx

    我想将 64 位地址压入堆栈 如下所示 asm pushq 0x1122334455667788 但我得到编译错误 我只能按以下方式推送 asm pushq 0x11223344 有人可以帮助我理解我的错误吗 我是装配新手 所以如果我的问题
  • C/C++ 中的简单“Hello World”内联汇编语言程序

    我使用 devcpp 和 borland c 编译器 asm mov ax 4 I O Func mov bx 1 Output func mov cx name address of the string mov dx 6 length
  • 装配中出现奇怪的字符?

    我写了以下代码 386 model small stack 100h data text db Paper 0 code start lea dx text mov ah 9h int 21h mov ah 4ch int 21h end
  • 汇编指令陷阱有什么作用?

    当程序需要时 程序通常会发出软件陷阱 由操作系统提供服务 通用异常处理程序 操作系统确定陷阱的原因并做出响应 适当地 汇编指令 trap 和 BASIC 中的 TRAP 指令一样吗 答案似乎是肯定的 你能接受还是拒绝我的结论 不中断 的代码
  • 一条指令可以同时处于两种寻址模式吗?

    我在书中读到了以下内容从头开始编程 处理器有多种不同的访问数据的方式 称为 寻址模式 最简单的模式是立即模式 其中 要访问的数据嵌入在指令本身中 例如 如果我们想将寄存器初始化为 0 而不是给出 计算机要从中读取 0 的地址 我们将指定立即
  • C++ 中的 CPUID 实现

    我想知道这里是否有人有一些可以从任何托管 net 语言引用的 C CPUID 实现的好示例 另外 如果情况并非如此 我是否应该注意 X86 和 X64 之间的某些实现差异 我想使用 CPUID 来获取运行我的软件的机器上的信息 崩溃报告等
  • AVX512 掩码寄存器(k1...k7)的 GNU C 内联 asm 输入约束?

    AVX512 为其算术命令引入了 opmask 功能 一个简单的例子 上帝螺栓 org https godbolt org z P7xWD8 include
  • 如何让c代码执行hex机器代码?

    我想要一个简单的 C 方法能够在 Linux 64 位机器上运行十六进制字节码 这是我的 C 程序 char code x48 x31 xc0 include
  • 汇编-符号标志和奇偶校验标志

    我不明白什么时候设置标志标志 什么时候设置奇偶校验 据我所知 符号标志表示运算结果的符号 0表示正数 1表示负数 那么为什么在下一个代码中 mov al 5 sub al 124 SF为零 结果是负数 关于PF 为什么a和b中设置了PF a
  • 为什么不能执行 mov [eax], [ebx] [重复]

    这个问题在这里已经有答案了 我可以做这个 mov eax ebx 和这个 mov eax ebx 甚至这个 mov eax ebx 但不是这个 错误C2415 mov eax ebx 只是wtf 为什么 它与 ptr1 ptr2 相同 为什
  • Visual Studio 2017 上的简单装配程序

    386 model flat c stack 100h printf PROTO arg1 Ptr Byte data msg1 byte Hello World 0Ah 0 code main proc INVOKE printf ADD
  • 近调用/跳转表并不总是在引导加载程序中工作

    一般问题 我一直在开发一个简单的引导加载程序 并在某些环境中偶然发现了一个问题 在这些环境中 此类指令不起作用 mov si call tbl SI Call table pointer call call tbl Call print c
  • 减法进位标志

    我正在使用 MASM32 有了这个代码 mov eax 5 sub eax 10 CF 状态标志将被设置 但使用我的铅笔和纸 我实际上看到 MSB 没有任何进位 是的 我知道从较少的数字中减去大的数字集CF 但我想知道为什么 因为使用这段代
  • 从汇编程序获取命令行参数

    通读 专业汇编语言书籍 似乎它提供了用于读取命令行参数的错误代码 我纠正了一点 现在它从段错误变成了读取参数计数 然后是段错误 这是完整的代码 data output1 asciz There are d params n output2
  • 在 x86-64 CPU 上通过交叉修改代码重现意外行为

    Question 对于可能在 x86 或 x86 x64 系统上触发意外行为的交叉修改代码有哪些想法 在这些系统中 交叉修改代码中的所有操作均已正确完成 但在执行处理器之前执行序列化指令除外修改代码 如下所述 我有一个 Core 2 Duo
  • 为什么我的空循环在 Intel Skylake CPU 上作为函数调用时运行速度是原来的两倍?

    我正在运行一些测试来比较 C 和 Java 并遇到了一些有趣的事情 在 main 调用的函数中 而不是在 main 本身中 运行具有优化级别 1 O1 的完全相同的基准代码 导致性能大约翻倍 我正在打印 test t 的大小 以毫无疑问地验
  • AVX-512CD(冲突检测)与原子变量访问有何不同?

    所以我在看他们展示了如何 void Histogram const float age int const hist const int n const float group width const int m const float o

随机推荐

  • 如何使用 Spark DataFrames 查询 JSON 数据列?

    我有一个 Cassandra 表 为简单起见 它看起来像 key text jsonData text blobData blob 我可以使用 Spark 和 Spark cassandra connector 为此创建一个基本数据框架 v
  • 从响应标头中删除“服务器:Apache”

    我想知道如何完全删除 apache 在响应中发送的服务器标头 最初 它显示完整的服务器信息 例如Server Apache Ubuntu 14 04 在响应标头中 但我在某处读到将其添加到 apache2 conf 中 ServerToke
  • 如何将手机与邮箱/密码验证关联?

    我正在尝试创建一个 flutter 应用程序 在其中使用电子邮件 密码创建用户后 将其保存在 firebase 中 然后用户输入发送 OTP 的电话号码 并在验证后登录 我的问题是 当这两个步骤完成后 Firebase 将创建两个单独的帐户
  • 使用 Serde 反序列化对象时,有没有办法省略包装器/根对象?

    我有以下对象 data id 1 name South America countries data id 122 name Brazil capital Brasilia 我想定义两个结构 Continent and Country 省略
  • 为什么 GetHashCode 位于 Object 类中?

    Why 获取哈希码是对象类的一部分吗 只有一小部分类的对象用作哈希表中的键 当我们希望类的对象作为哈希表中的键时 拥有一个必须实现的单独接口不是更好吗 MS 团队决定将此方法包含在对象类中 从而使其 随处 可用 肯定是有原因的 在我看来 这
  • 如何将当前屏幕捕获为图像?

    我想添加用户在我的应用程序中捕获当前屏幕并通过电子邮件发送的功能 我有一个非常非技术性的用户群 所以我需要它尽可能简单 我打算让他们单击名为 帮助我 的菜单项 然后 它将捕获当前的应用程序屏幕 最好是 jpg 或 png 格式 然后打开 O
  • Git 接收后挂钩不起作用

    我的设置是 Windows XAMPP 服务器 启用了 cURL 并安装了 Git 和 Hudson Hudson 每分钟轮询一次 Git 以查找更改 如果找到更改 就会创建一个构建 我使用这个版本作为我的测试服务器 这很好用 我想在我的中
  • Chart.js 压缩条形图上的垂直轴

    我有一个数据集 其中最后一个值总是非常高 这会导致我的条形图出现问题 几乎所有其他值如果不悬停在它们上面就很难有感觉 这是一个屏幕截图 这就是我想要达到的目标 所以我的问题是 这可以在普通 Chart js 中实现吗 或者我需要一个插件吗
  • 在 JavaScript (node.js) 中设置错误原因

    是否可以指定 JavaScript node js 中错误的原因 我找到了Mozilla 文档它定义了如何设置消息 文件 行 但不定义错误原因 我对此感兴趣的原因是我想捕获内部错误并将其传播到嵌套异常中的表面 类似于 Java 中的异常链
  • 如何从类似文件的对象中使用 python mimetypes.guess_type

    gt gt gt mimetypes guess type picture jpg image jpeg None 现在我有一个类似文件的对象 例如 stingIO 哪些内容是图像的数据 我如何从类似文件的对象中检测 mimetypes p
  • 显示内存中的 RGBA 图像

    我有一个包含 RGBA 编码图像的 C 字节数组 在 WPF 中显示此图像的最佳方式是什么 一种选择是从字节数组创建 BitmapSource 并将其附加到 Image 控件 但是 创建 BitmapSource 需要 RGBA32 的 P
  • Django 模板无法循环defaultdict

    import collections data firstname John lastname Smith firstname Samantha lastname Smith firstname shawn lastname Spencer
  • 如何捕获 Windows Azure(辅助)角色中未处理的异常

    我试图捕获我的工作角色中所有未处理的异常 我尝试放一个try catch块进入Run 方法 按照建议here 但没有成功 public override void Run try base Run catch Exception ex Tr
  • 检查 Python 中是否存在某种*类型*的文件

    我意识到这看起来与检查文件是否存在的其他问题类似 但它是不同的 我正在尝试找出如何检查type文件存在 如果不存在则退出 我最初尝试的代码是这样的 filenames os listdir os curdir for filename in
  • 如何使用composer安装部分git存储库?

    我只想需要 git 存储库的子部分 而不是完整的东西 我想这样做的原因是因为存储库很大 就我而言 存储库是 https github com pubnub pubnub api git我只想要 php 目录 我在作曲家中定义了以下包 typ
  • OpenCV功能类似于matlab的“查找”

    我正在寻找 openCV 中的一个函数来帮助我制作图像蒙版 例如在 MATLAB 中 B A or B 零 大小 A B A 10 c 有些功能可以让你通过mask向他们提出论据 按照您描述的方式创建面具 我认为您正在追求Cmp 或 Cmp
  • 检查 Intent.ACTION_VIEW 上的用户操作

    我有一个填充了一些文件的列表视图 可以有多种类型 如 pdf 或文档 当用户单击一个文件时 我会获取文件 mime 类型并启动一个意图 让用户选择使用哪个应用程序来打开该文件 什么我想知道用户是否选择了某些内容 或者只是按回键而没有选择任何
  • 在 python 中填充队列并管理多处理

    我在 python 中遇到这个问题 我有一个 URL 队列 需要时不时地检查一下 如果队列已满 我需要处理队列中的每个项目 队列中的每个项目必须由单个进程处理 多处理 到目前为止 我设法 手动 实现这一点 如下所示 while 1 self
  • 带有组合 API 的 Vue 3 事件总线

    我已经设置了mitt并尝试将事件分派到另一个组件 但我遇到了困难 因为在setup 它没有的方法this用于访问应用程序实例 这是我尝试过的 import App from App vue const el document getElem
  • 了解用于函数调用的 auipc+jalr 序列

    我试图阅读RISC V生成的程序集gcc我发现gcc创建序列auipc jalr对于某些函数调用 我不明白它是如何工作的 这是一个简单的例子 考虑以下C源文件 unsigned long id unsigned long x return