vimer linux kernel 爱好者

实现一个riscv链接器1

2022-10-01

这是PLCT实验室的一个公开课程,挺不错的,课程主页 或者 github.

搭建环境

docker run -u root --volume ~/build/go_ld:/code -it golang:bullseye 

上面这个命令就是快速使用docker搭建一个友好的开发环境,volume参数可以将宿主机的环境映射到docker中。

Digest: sha256:2a80cda19fac1972f4c8d69516372b452062631f84854c223f1a164ce77b06d3
Status: Downloaded newer image for golang:bullseye
root@57403975e64f:/go# cd /code/
root@57403975e64f:/code# ls

然后安装工具:

apt install gcc-10-riscv64-linux-gnu qemu-user

简化一下命令:

ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc

exit退出容器。

docker补充知识

1.
docker ps -a
docker start names(容器名字)
2. 进入容器
A. docker attach name/id 使用“docker attach”命令进入container(容器)有一个缺点,那就是每次从container中退出到前台时,container也跟着退出了。
B. docker exec -it  (退出后台运行)

安装依赖

首先在容器中安装以下软件:

apt install gcc-10-riscv64-linux-gnu qemu-user
# 简化一下:
ln -sf /usr/bin/riscv64-linux-gnu-gcc-10 /usr/bin/riscv64-linux-gnu-gcc 

写一个简单的go文件测试下环境:

package main

func main()  {
	println("hello,world")
}

因为映射了嘛,所以可以直接在~/code下写go文件,然后在容器中运行。

go run rvld.go
hello,world

go mod 管理

root@57403975e64f:/code/rvld# go mod init github.com/yuzibo/go_ld 
go: creating new go.mod: module github.com/yuzibo/go_ld
go: to add module requirements and sums:
        go mod tidy
root@57403975e64f:/code/rvld# go mod tidy 

接下来写一个c程序,使用 -c只生成目标文件,不链接。

riscv64-linux-gnu-gcc main.c -c -o main.o 

不过这样可能不方便,所以我们需要新建一个tests的目录。

现在归纳一下环境就是,在宿主机上目前为~/build/go_ld而在docker里面为/code/

issue Error: EACCES: permission denied

如果vscode报告权限问题的话,可以参考这里看一下: here

chmod -R 777 dir # is ok

test case demo

在rvld/tests目录下,使用以下demo,就可以快速改写自己的测试文件。

#!/bin/bash

echo "$0"
test_name=$(basename "$0" .sh)
t=out/tests/$test_name

mkdir -p "$t"
cat <<EOF | riscv64-linux-gnu-gcc -o $t/a.o  -c -xc -
#include <stdio.h>

int main(){
    printf("hello,world");
    return 0;
}
EOF

./go_ld "$t/a.o"
# 在测试文件中直接调用rvld,因为一开始没有使用 go mod,这里导致go的可执行文件不一致

测试:

package main

import (
	"os";
	"fmt"
)

func main()  {
	fmt.Printf("%v\n", os.Args)
	if len(os.Args) < 2{
		println("wrong args")
		os.Exit(1)
	}
}

并执行测试脚本:

oot@57403975e64f:/code/rvld#  go build rvld.go 
root@57403975e64f:/code/rvld# ./tests/hello.sh 
./tests/hello.sh
[./rvld out/tests/hello/a.o]

下面写一个Makefile再次简化这个流程:

TESTS := $(wildcard tests/*.sh)

build:
        go build

test: build test_files
        @printf '\e[32mOKpassed all tests\e[0m\n'

test_files:
        @echo 'testing'
        @for file in $(TESTS); do \
                ./$$file ; \
        done
        @printf '\e[32mOK\e[0m\n'

clean:
        go clean
        rm -rf out/

.PHPNY: build clean test $(TESTS)

由于间隔时间比较远,所以有些混乱,目前的目录结构如下 :

vimer@dev:~/build/go_ld/rvld$ tree
.
├── go-ld.go
├── go.mod
├── Makefile
├── pkg
│   └── utils
│       └── utils.go # 抽象出来
└── tests
    ├── hello.sh
    └── world.sh

3 directories, 6 files

开始解析


Comments

Content