文件列表

编译

本章的学习尽量要用纯C的代码,不要写C++!不要写C++!不要写C++!
本章节配套视频:哔哩哔哩


环境准备

riscv的编译链环境已经准备好了,拉取环境:

docker pull registry.cn-hangzhou.aliyuncs.com/loongenclave/riscv-basic:12.2.0

当使用docker创建好环境之后,假设创建的continer id 是 CONTINER_ID,那么要进入该容器的方法是:

docker exec -it $CONTINER_ID zsh
# 因为我把默认的终端从bash改成了zsh,因此如果你使用bash进入的话会失败

--

提供的编译链如下:

你们要使用的编译链的前缀是:riscv64-unknown-linux-gnu-,不要使用riscv64-unknown-elf-


交叉编译

交叉编译入门指南


作业

--

报告

所有的编译链,没有特殊说明,都是使用的riscv编译链,且是使用gcc编译的。

本章的学习尽量要用纯C的代码,不要写C++!不要写C++!不要写C++!

--

测试代码:

// test.c
int a = 1, b = 0, c = 100, d;
static int aa = 1, bb = 0, cc = 100, dd;
const int aaa = 1, bbb = 0, ccc = 100;
static const int aaaa = 1, bbbb = 0, cccc = 100;

int add(int a, int b)
{
    return a + b;
}

int sub(int a, int b)
{
    return a - b;
}

int main()
{
    int a2 = 1, b2 = 0, c2 = 100, d2;
    static int aa2 = 1, bb2 = 0, cc2 = 100, dd2;
    const int aaa2 = 1, bbb2 = 0, ccc2 = 100;
    static const int aaaa2 = 1, bbbb2 = 0, cccc2 = 100;
    a2 = sub(a2, b2);
    return 0;
}

--

  1. 在编译链中,file、ldd、gcc、g++、ld、nm、objcopy、objdump、strings、strip、readelf等工具的作用是什么?哪些是编译链的一部分,哪些不是?

  2. elf文件与bin文件的区别
    --

  3. 详细阐述gcc的编译过程,需要动手实践截图说明。

  4. 以测试代码为例,绘制一幅二进制文件的layout,清楚地指出各个段的起始地址、结束地址,描述各个段的作用,分析变量的初始值是什么?(提示,在使用gcc编译时,请务必指定O0,要求使用riscv的gcc进行编译)

  5. 如何反汇编数据段,如何反汇编代码段?

--

  1. 了解二进制文件与进程,回答下面问题:
    1. 二进制文件与进程的联系与区别?
    2. 什么是栈空间,什么是堆空间?在什么情况下数据会被分配到堆空间,什么情况下数据被分配到栈空间?在编译链接时也会分配数据段,那么数据段跟栈空间、堆空间有联系吗?
    3. 分析所有变量所在的空间(分为两种情况,二进制文件时和进程运行时)

--
栈空间
函数调用之寄存器传参

见视频,精准降落

--

  1. 什么是交叉编译?如何实现交叉编译?对问题1中的代码进行交叉编译,自行编写CMakeLists.txt和Makefile
    note:
prefix=riscv64-unknown-linux-gnu-
cc?=${prefix}gcc
cflags= -nostdlib
output=test-nostdlib


bin:
	${cc}   test.c -o ${output}

asm:
	${cc} ${cflags} -S test.c -o ${output}.s

dump: bin
	${prefix}objdump -d ${output} > ${output}.dump.s

dump-sbss: bin
	${prefix}objdump -j .sbss -d ${output} > ${output}.dump.sbss.s

header:
	${prefix}objdump -h ${output}
	# ${prefix}readelf -S ${output}


all: bin asm dump

clean:
	rm -f ${output}
	rm -f *.s

--

  1. 什么是标准库,在哪里可以找到标准库(x86的标准库和riscv的标准库分别在哪里)?问题1中的代码在编译时可以不使用标准库吗?请分析在使用和不使用标准库的情况下二进制文件的段布局有何异同?请绘制出代码段的layout对比图。
  2. 在编译时,指定或者不指定-nostdlib时,使用make asm 生成的.s文件有区别吗?使用make dump得到的文件有区别吗?为什么?

--

  1. (x86_64编译器)什么是静态链接,什么是动态链接?把问题1中的代码,add函数实现在静态库中,sub函数实现在动态库中,同样要求自行编写CMakeLists.txtMakefile。要求程序能够正常运行,自行添加测试代码。

--

  1. gcc在编译程序时可以指定第三方库,可以通过以下方式指定库的路径,主要有哪些方法?PS:-Wl,rpath方法有什么特殊之处?

--

  1. 输入x86_64-linux-gnu-gcc -vriscv64-unknown-linux-gnu-gcc -v,观察会有哪些输出

  2. 在gcc编译命令中,sysroot参数的作用是什么?如何指定sysroot?

  3. 使用file命令查看/usr/bin/find的架构,并使用合适的工具分析/usr/bin/find的段信息,并绘制段layout图。(PS:哪些段在加载到内存中时需要进行清零?)

--

实验

--

ELF解析器

在x86下实现,不需要使用交叉编译链

  1. /usr/bin/find拷贝到实验文件夹下,并命名为find-bak

  2. 使用mmap函数将find-bak二进制文件映射到内存中

  3. 实现对elf header的解析

--

编译riscv交叉编译链(可选)

GitHub - riscv-collab/riscv-gnu-toolchain: GNU toolchain for RISC-V, including GCC


参考资料

请 Ta 喝咖啡 ☕️