diff --git a/articles/20230929-elf2flt-fix-install.md b/articles/20230929-elf2flt-fix-install.md new file mode 100644 index 0000000000000000000000000000000000000000..9fd4f9731ef6de396eb5287df82422747faa23ce --- /dev/null +++ b/articles/20230929-elf2flt-fix-install.md @@ -0,0 +1,189 @@ +> Corrector: [TinyCorrect](https://gitee.com/tinylab/tinycorrect) v0.2-rc2 - [spaces codeblock codeinline pangu]
+> Author: Odysseus <320873791@qq.com>
+> Date: 2023/09/19
+> Revisor: walimis <>
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+> Proposal: [为 ELF2FLT 完善独立编译与安装支持](https://gitee.com/tinylab/riscv-linux/issues/I79PO2)
+> Sponsor: PLCT Lab, ISCAS + +# elf2flt 在 RISC-V 64 位下的独立编译和安装支持 + +## 前言 + +elf2flt 在设计之初是与 Buildroot 工具链结合使用的,但是它实际上也能搭配本机自带的工具链(例如 `/usr/bin/gcc`)使用。然而,原先的 elf2flt 安装过程存在问题,使得它无法正确安装到本机,与本机工具链结合使用。下面我们以 riscv-lab 下的安装过程为例,来看看其原因何在。 + +## 过程 + +首先我们克隆下来原来的 git 仓库,然后开始构建: + +```sh +$ git clone https://gitee.com/tinylab/elf2flt +$ cd elf2flt +$ ./configure --target=riscv64-linux-gnu -with-bfd-include-dir=/usr/include/ \ +> --with-binutils-include-dir=/usr/include/ \ +> --with-libbfd=/usr/lib/riscv64-linux-gnu/libbfd.a \ +> --with-libiberty=/usr/lib/riscv64-linux-gnu/libiberty.a \ +> --disable-werror --build=riscv64-linux-gnu +$ make +$ sudo make install +``` + +它会报错,如下: + +```sh +/usr/bin/install -c -d /usr/local/bin /usr/local/riscv64-linux-gnu/bin /usr/local/riscv64-linux-gnu/lib +/usr/bin/install -c -m 755 flthdr /usr/local/bin/riscv64-linux-gnu-flthdr +/usr/bin/install -c -m 755 flthdr /usr/local/riscv64-linux-gnu/bin/flthdr +/usr/bin/install -c -m 755 elf2flt /usr/local/bin/riscv64-linux-gnu-elf2flt +/usr/bin/install -c -m 755 elf2flt /usr/local/riscv64-linux-gnu/bin/elf2flt +[ -f /usr/local/bin/riscv64-linux-gnu-ld.real ] || \ + mv /usr/local/bin/riscv64-linux-gnu-ld /usr/local/bin/riscv64-linux-gnu-ld.real +/usr/bin/mv: cannot stat '/usr/local/bin/riscv64-linux-gnu-ld': No such file or directory +make: *** [Makefile:114: install] Error 1 +``` + +这里我们可以对应到原 `Makefile.in` 的以下部分: + +```makefile +install: + $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(target_bindir) $(DESTDIR)$(target_libdir) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(target_bindir)/$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_ELF2FLT) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(target_bindir)/$(PROG_ELF2FLT) + [ -f $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) + [ -f $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(target_bindir)/ld$(EXEEXT) $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(target_bindir)/ld$(EXEEXT) + $(INSTALL) -m 644 $(SRC_LDFILE) $(DESTDIR)$(target_libdir)/$(LDFILE) +``` + +这个 `$(bindir)` 和 `$(target_bindir)` 分别是什么呢?我们在 `Makefile.in` 中寻找,发现它们的值如下: + +```makefile +bindir = @bindir@ + +# ... + +target_bindir = $(prefix)/$(TARGET)/bin +``` + +这个 `$(prefix)` 在 `./configure` 中被赋默认值: + +```sh +ac_default_prefix=/usr/local + +# ... + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' +``` + +因此这里的 `riscv64-linux-gnu-ld` 默认会在 `/usr/local/bin` 下查找,但是那里显然没有,这意味着我们很可能要创建一个软链接,或者直接将别处 `riscv64-linux-gnu-ld` 复制到该目录下面。我们使用 `which` 命令查看我们的交叉工具链在哪里: + +```sh +$ which riscv64-linux-gnu-gcc +/usr/bin/riscv64-linux-gnu-gcc + +$ which riscv64-linux-gnu-ld +/usr/bin/riscv64-linux-gnu-ld +``` + +可以看到工具链调用的是 `/usr/bin` 下的。这里每个人的情况可能会有所不同,后面可能会考虑在配置文件中增加工具链路径的配置选项。 + +我们对此做出如下修改: + +```diff +install: + $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(target_bindir) $(DESTDIR)$(target_libdir) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(target_bindir)/$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_ELF2FLT) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(target_bindir)/$(PROG_ELF2FLT) ++ cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(bindir)/$(TARGET)-ld ++ cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(target_bindir)/ld + [ -f $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) ++ mv /usr/bin/$(TARGET)-ld /usr/bin/$(TARGET)-ld_old ++ ln -s $(DESTDIR)$(target_bindir)/ld /usr/bin/$(TARGET)-ld + $(INSTALL) -m 644 $(SRC_LDFILE) $(DESTDIR)$(target_libdir)/$(LDFILE) +``` + +另外,在 `$(bindir)/` 下的 elf2flt 和 flthdr 实际上是带前缀的,但是它们在被调用时是不带前缀的,因此会出现形如 `ld (ld-elf2flt): execvp: No such file or directory` 的错误。这里需要把链接加上: + +```diff +install: + $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(target_bindir) $(DESTDIR)$(target_libdir) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(target_bindir)/$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_ELF2FLT) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(target_bindir)/$(PROG_ELF2FLT) + cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(bindir)/$(TARGET)-ld + cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(target_bindir)/ld + [ -f $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) + [ -f $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(target_bindir)/ld$(EXEEXT) $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(target_bindir)/ld$(EXEEXT) + mv /usr/bin/$(TARGET)-ld /usr/bin/$(TARGET)-ld_old + ln -s $(DESTDIR)$(target_bindir)/ld /usr/bin/$(TARGET)-ld ++ ln -s $(DESTDIR)$(bindir)/$(TARGET)-elf2flt$(EXEEXT) $(DESTDIR)$(bindir)/elf2flt$(EXEEXT) ++ ln -s $(DESTDIR)$(bindir)/$(TARGET)-flthdr$(EXEEXT) $(DESTDIR)$(bindir)/flthdr$(EXEEXT) + $(INSTALL) -m 644 $(SRC_LDFILE) $(DESTDIR)$(target_libdir)/$(LDFILE) +``` + +到了这一步重新进行安装,过程看起来比较顺利。然而我们编译一个简单的 hello-world 时,仍然发生了 `ld (ld-elf2flt): execvp: No such file or directory` 的错误,如下: + +```sh +$ riscv64-linux-gnu-gcc -nostdinc -nostdlib -I/usr/bin/../lib/gcc/riscv64-linux-gnu/11/include -I/usr/bin/../lib/gcc/riscv64-linux-gnu/11/include-fixed -I/labs/riscv-lab/buildroot/output/host/riscv64-buildroot-linux-uclibc/sysroot/usr/include /labs/riscv-lab/buildroot/output/build/uclibc-1.0.44/lib/crt1.o /usr/lib/gcc/riscv64-linux-gnu/11/crti.o /usr/lib/gcc/riscv64-linux-gnu/11/crtbeginT.o -L/usr/lib/gcc/riscv64-linux-gnu/11 -L/usr/lib/gcc -L/labs/riscv-lab/buildroot/output/build/uclibc-1.0.44/lib -Wl,--build-id=none -Wl,-elf2flt=-r -o hello-b-gcc hello.c --static -lc -lgcc /usr/lib/gcc/riscv64-linux-gnu/11/crtend.o /usr/lib/gcc/riscv64-linux-gnu/11/crtn.o +ld (ld-elf2flt): execvp: No such file or directory +collect2: error: ld returned 1 exit status +``` + +经查,发现原因是 `$(target_bindir)/` 下面没有程序 `nm`,导致 `ld` 在调用它时出现问题。因此我们加上一句链接: + +```diff +install: + $(INSTALL) -d $(DESTDIR)$(bindir) $(DESTDIR)$(target_bindir) $(DESTDIR)$(target_libdir) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_FLTHDR) $(DESTDIR)$(target_bindir)/$(PROG_FLTHDR) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-$(PROG_ELF2FLT) + $(INSTALL) -m 755 $(PROG_ELF2FLT) $(DESTDIR)$(target_bindir)/$(PROG_ELF2FLT) + cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(bindir)/$(TARGET)-ld + cp /usr/bin/$(TARGET)-ld $(DESTDIR)$(target_bindir)/ld + [ -f $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) $(DESTDIR)$(bindir)/$(TARGET)-ld.real$(EXEEXT) + [ -f $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) ] || \ + mv $(DESTDIR)$(target_bindir)/ld$(EXEEXT) $(DESTDIR)$(target_bindir)/ld.real$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(bindir)/$(TARGET)-ld$(EXEEXT) + $(INSTALL) -m 755 $(PROG_LD_ELF2FLT) $(DESTDIR)$(target_bindir)/ld$(EXEEXT) + mv /usr/bin/$(TARGET)-ld /usr/bin/$(TARGET)-ld_old + ln -s $(DESTDIR)$(target_bindir)/ld /usr/bin/$(TARGET)-ld + ln -s $(DESTDIR)$(bindir)/$(TARGET)-elf2flt$(EXEEXT) $(DESTDIR)$(bindir)/elf2flt$(EXEEXT) + ln -s $(DESTDIR)$(bindir)/$(TARGET)-flthdr$(EXEEXT) $(DESTDIR)$(bindir)/flthdr$(EXEEXT) ++ ln -s /usr/bin/$(TARGET)-nm $(DESTDIR)$(target_bindir)/nm + $(INSTALL) -m 644 $(SRC_LDFILE) $(DESTDIR)$(target_libdir)/$(LDFILE) +``` + +至此问题解决,`riscv64-linux-gnu-gcc` 能够编译出正常的 bFLT 程序: + +```sh +$ riscv64-linux-gnu-gcc -nostdinc -nostdlib -I/usr/bin/../lib/gcc/riscv64-linux-gnu/11/include -I/usr/bin/../lib/gcc/riscv64-linux-gnu/11/include-fixed -I/labs/riscv-lab/buildroot/output/host/riscv64-buildroot-linux-uclibc/sysroot/usr/include /labs/riscv-lab/buildroot/output/build/uclibc-1.0.44/lib/crt1.o /usr/lib/gcc/riscv64-linux-gnu/11/crti.o /usr/lib/gcc/riscv64-linux-gnu/11/crtbeginT.o -L/usr/lib/gcc/riscv64-linux-gnu/11 -L/usr/lib/gcc -L/labs/riscv-lab/buildroot/output/build/uclibc-1.0.44/lib -Wl,--build-id=none -Wl,-elf2flt=-r -o hello-b-gcc hello.c --static -lc -lgcc /usr/lib/gcc/riscv64-linux-gnu/11/crtend.o /usr/lib/gcc/riscv64-linux-gnu/11/crtn.o +$ +``` + +## 总结 + +elf2flt 独立编译和安装存在的问题主要是安装部分,因此在修改 `Makefile.in` 后得以解决。消除 Buildroot 的依赖则是另一个问题,需要仔细观察 Buildroot 交叉编译时依赖了哪些库和头文件,并且想办法去除这些依赖,这里不详细说明。 + +## 参考资料 + +- [https://gitee.com/tinylab/riscv-linux/blob/master/articles/20230919-elf2flt-flt-analysis-0.md][001] +- [https://gitee.com/tinylab/elf2flt][002] + +[001]: https://gitee.com/tinylab/riscv-linux/blob/master/articles/20230919-elf2flt-flt-analysis-0.md +[002]: https://gitee.com/tinylab/elf2flt