我们介绍了 RISC-V 的指令,你可以当作介绍了汇编语言。但是,我们现在知道的是:

  • RV32I 的格式都是 32bit 的
  • 以上内容可以以 beq 等格式让读者可读,但是机器执行的还是那6种格式的代码

我们也了解了 RISC-V 的 calling convention 和 ABI, 这一节介绍程序的编译、链接、加载。基础知识可以阅读 CSAPP 第七章和 https://zhuanlan.zhihu.com/p/125163040 我之前写的垃圾。不过今天我写的会细一些。

编译

编译由 .c 转为汇编语言,形式是 .s, 这个我们之前都用过了

✗ riscv64-unknown-elf-gcc -march=rv32imac -mabi=ilp32 -S

这样体验一把就行。Compiler 需要走:

  • Lexer: 语法分析,把目标转成 token. 实际上可以借用 Lex 工具,而 Flex 是 Lex 的一个实现。
  • Parser: 将内容变为 AST
  • Semantic Analysis and Optimization: 检查 AST, 然后做一些优化
  • Code generation: 做寄存器 allocation, 代码生成

实际上,我们生成 -S 也需要指定编译选项,能指定 -O。这里我们可以得到可靠的 RISC-V 的代码。它和我们的程序是逻辑上等价的,当然可能要进行一定的优化。

注意,在 RISC-V 中,编译是会产生便于理解的伪指令的。

汇编

将汇编语言生成 ELF 的 object file, object file 属于 machine language 了。

ELF 文件包括:

  • ELF Header, 以一个 16byte 的序列开始,描述系统 word 大小、字节顺序等
  • .text text segment, 编译程序的机器代码
  • .data 已初始化的 global/static C variable, 即源代码的 static 部分。
    • Local 是在 stack 中的
    • 未初始化的、被初始化为 0 的,在 .bss 中。它不占据实际空间,有点类似 gcc 的 __weak__ .
  • .symtab ,符号表,存放定义、引用的函数、全局变量和不可被 reference 的 static 变量
  • .debug 调试符号表,包含原始文件; .line 同样,包含行号和 .text 的映射。只有 -g 编译才会产生
  • .strtab 字符串表,包含定义的 string 和 section 的名字。

ELF 具体信息可以看:

那么我们还要注意,有的 directions, 即汇编指示符,会被丢给链接器,但不产生什么代码

  1. .text user text segment 中的片段
  2. .data 需要写到 user data segment 中的片段
  3. .globl sym 可以从其他文件引用的全局符号
  4. .string str 把对应的 C-Style 字符串存在内存中
  5. .word w1...wn 把这 n 个连续的符号连续存取

同时,在链接的时候,会完成伪指令的替换,把它们全部替换成具体的指令(这里可以表示 RV32I 这样的)。

我们来看看 hello world:

#include <stdio.h>
int main() {printf("Hello, %s", "world");return 0;
}

在 RISC-V Book 上,它生成了如下的汇编:

其中图 3.6 中用到的指示符有:

  • .text:进入代码段。
  • .align2:后续代码按22字节对齐。
  • .globl main:声明全局符号“main”。
  • .section .rodata:进入只读数据段
  • .balign4:数据段按4字节对齐。
  • .string “Hello, %s!n”:创建空字符结尾的字符串。
  • .string “world”:创建空字符结尾的字符串。

tail call optimization

fn:return foo(x)

这个时候,正常行为应该是:把 a0 设置 x, 然后返回调用后的 a0, 即:

  • 然后用 j 或者 tail 直接调用 foo(y) ,这玩意会做个保存,把本函数的 ra, sp 保存,这样跳转的话就可以直接跳转到 fn 的调用者。

在 RISC-V 伪指令中,有一条 tail, 会被解释成

auipc x6, offset[32:12]
jalr x0, x6, offset[11:0]

tailj 来完成上述的 jump,而不再调用前再去设 sp/ra

这个行为让我有些头晕,我在网上找到了这篇 blog: https://www.sifive.com/blog/all-aboard-part-3-linker-relaxation-in-riscv-toolchain

上面的链接倒是介绍的比较清楚。

生成机器代码

我们描述过 ELF 格式了,我们有下列几个问题

压缩指令

RV32C 支持压缩指令:

  • 每条短指令长度为 16bits
  • 必须和 32bits 指令一一对应
  • 只对汇编器和连接器可见,并且是否以短指令取代对应的宽指令由 它们决定。编译器编写者和汇编语言程序员可以幸福地忽略 RV32C 指令及其格式,他们能 感知到的则是最后的程序大小小于大多数其它 ISA 的程序。

RV32C 如上

那么,考虑压缩指令,会有下列问题:

So the presence of the 16b instructions doesn't need to be known to anybody but the assembler and the RISC-V processor itself!

Forward Reference

即假设 L1 --> L2, 但 L1 在 L2 之前,那么这暗示编译器需要一张局部的符号表,并扫描不止一个 pass,来完成这个操作。

jal/la static 加载

jal 会跳转一个 imm, 而 static 变量加载中,可能对应的符号来自另一个文件定义的内存中。

Tables

为了解决上述问题,有了 symbol table 和 relocation table:

symbol table 展示可能被其他文件用到的本文件符号,例如 function call 的 label, 和 .data 中可以被外部访问的符号。

Relocation Table 展示 jalla 中需要重定位的地址。

链接

讲 ELF 的 objective code 转化为可执行文件,这一过程被称为 linking, 这一过程有逻辑上如下的流程:

  • .o 文件把 text segment 合在一起
  • 拿到 data segment, 拼接到一起
  • resolve reference, 解决掉跨文件的符号、依赖问题,用绝对的地址填充

实际上,beq bne jal 这类 PC-relevant 的指令不会被 relocate, 而用 name/label 相关的和 static 的会 relocate.

加载

通常 OS 会加载、运行程序:

它需要:

  1. 读 executable 文件,加载 ELF,来知道 text 和 data 的大小
  2. 创建带 stack、text 的地址空间
  3. 把 instruction 和 data 拷贝到新的地址空间
  4. 拷贝用户的参数,传到栈上,供程序运行
  5. 初始化寄存器
  6. 跳转到用户程序,并设置 PC

全流程

#include <stdio.h>
int main(int argc, char* argv[]) {int i;int sum = 0;for (i = 0; i <= 100; i++) sum += i * i;printf("The sum of sq from 0 .. 100 is %dn", sum);
}

编译后生成:

assembly 的时候处理伪指令:

assembly 的时候生成 symbol table 和 relocation table:

以上的信息在链接的时候一起使用。

动态链接库和静态链接库

对于静态库而言,它是可执行文件的一部分,库更新了,运行中的程序需要重新编译。这是编译时链接的。

在 Linux 下,提供了 .a 文件,用于处理,单个文件即使没有用到所有部分,也需要全部加载。

在动态链接库中,允许编译时、运行时链接。

共享库有 .so 文件,引用库的用户可以共享这些数据,而在内存中,共享库的.text 可以共享内存,被多个进程使用:

CSAPP 中,指导可以在 dlfcn.h 中使用该功能。

此外,为了共享,需要处理位置无关代码(Position-Independent Code, PIC)。这需要 -fPIC 选项。

它在编译时成功设置一个便宜量,并在运行时不改变这个便宜量,让代码能够运行便宜量上的 .text

而 PLT 条目类似懒惰加载,作为链接的表来完成工作:

cygwin编译生成hello world_RISC-V 入门 Part4: 编译、链接、加载相关推荐

  1. cfile清空文件内容_编译-链接-加载 :ELF文件格式解析

    摘要:对于C++的初学者,经常在程序的编译或者加载过程中遇到很多错误,类似undefined reference to ... 和 GLIBCXX_3.4.20 not found 等.这些错误都涉及 ...

  2. Linux下C/C++程序编译链接加载过程中的常见问题及解决方法

    Linux下C/C++程序编译链接加载过程中的常见问题及解决方法 1 头文件包含的问题 报错信息 该错误通常发生在编译时,常见报错信息如下: run.cpp:2:10: fatal error: dl ...

  3. C++程序编译-链接-加载过程初探-符号表

    文章目录 前言 符号表 nm指令 看一下hello world 变量和函数在符号表中的位置 看看类的定义 把类和主程序一起打包测试 符号表角度看编译与链接 加载程序到内存 前言 从自己接触计算机程序设 ...

  4. java 类编译_Java类编译、加载、和执行机制

    Java类编译.加载.和执行机制 标签: java 类加载 类编译 类执行 机制 0.前言 个人认为,对于JVM的理解,主要是两大方面内容: Java类的编译.加载和执行. JVM的内存管理和垃圾回收 ...

  5. Linux编译mplayer播放badapple及编译fbv加载图片(基于V3S预告板子要开源了)

    Linux编译mplayer播放badapple及编译fbv加载图片(基于V3S预告板子要开源了) 1.编译前准备: 1.linux5.10内核 2.2018.02bulidroot 3.v3s板子 ...

  6. Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示

    Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展示 目录 ​Three 之 three.js (webgl)基础 第二个入门案例之汽车模型加载和简单模型展 ...

  7. leaflet-webpack 入门开发系列二加载不同在线地图切换显示(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载 webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 ...

  8. 反编译所有图片加载库,让OOM无所遁形!

    码个蛋(codeegg) 第 989 次推文 作者:ZhouZhengyi 链接:https://juejin.im/post/5ea1c46851882573a25f3ec3 1.背景 最近看滴滴开 ...

  9. linux 卸载模块命令,Linux中module模块的编译、加载、卸载

    在Linux下建个DriverHello目录,在DriverHello目录下编写源文件hello.c和Makefile文件. 1.编写源文件 hello.c #include #include#inc ...

最新文章

  1. 实名羡慕!蚂蚁员工激励达 1376.9 亿,人均能在杭州买套 283 平的房子?
  2. 简约代码表白_JS实现520 表白简单代码
  3. ALE IDoc RFC of SAP
  4. java vue20.2.2浏览器_支持 Java 15!Java 开发工具 IntelliJ IDEA 2020.2发布
  5. 国内是否可以安装alexa_Alexa可以听到您无法听到的命令,哪些黑客可以利用
  6. python数据结构与算法第六讲_Python 学习 -- 数据结构与算法 (六)
  7. java 进程不关闭_java运行程序关不了窗口
  8. transporter上传卡正在交付_Xcode11,Transporter上传卡在——正在验证 APP - 正在通过App Store进行认证...
  9. python排版word文档 效率_5秒搞定Word排版,效率颠覆你的想象!
  10. ASCII对应码表(键值)
  11. python调用QQ音乐API
  12. Excel中将角度与弧度之间相互转换的公式
  13. 在线招聘风起,周伯通怎么玩社区招聘?
  14. java满天星星代码_纯Java代码实现流星划过天空
  15. WIN7 嵌入式系统安装教程 Windows Embedded Standard 2011 安装
  16. 狙击美佐 基于WordPress的个人博客网站
  17. python随机数种子seed()的讲解
  18. 【图片】笔记图片资源
  19. MiniGPT4,开源了
  20. vue-awesome-swiper 传参控制滑动位置 滚动位置 slideTo 备注防止后期忘记

热门文章

  1. 岗位推荐 | 腾讯音乐娱乐招聘推荐算法工程师、推荐后台工程师
  2. android 定时语音,android 定时语音天气播报
  3. Dubbo启动时qos-server can not bind localhost:22222错误解决
  4. php es6写法,Web/PHPStorm ES6 语法支持以及实时编译
  5. Spring-aop注解开发(切点表达式的抽取)
  6. JavaScript——易班优课YOOC课群在线测试自动答题解决方案(五)简单插件
  7. Spring Boot + IntelliJ IDEA——自动部署[Spring Boot热部署]+LiveReload[前端自动刷新、热加载]解决方案
  8. Git——Git基本教程
  9. Codeforces 1196C Robot Breakout
  10. C/C++控制台应用程序——画三角形、圆、直线、矩形