rcore-os / rCore-Tutorial-Book-v3

A book about how to write OS kernels in Rust easily.
https://rcore-os.github.io/rCore-Tutorial-Book-v3/
GNU General Public License v3.0
1.23k stars 230 forks source link

rCore-Tutorial-Book-v3/chapter1/4first-instruction-in-kernel2 #96

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

内核第一条指令(实践篇) — rCore-Tutorial-Book-v3 3.6.0-alpha.1 文档

https://rcore-os.github.io/rCore-Tutorial-Book-v3/chapter1/4first-instruction-in-kernel2.html

CENZONGJUN commented 2 years ago

虽然老师提供了连接脚本,但是该怎么获知写的程序都有哪些段需要被链接呢?

bswaterb commented 2 years ago

开篇提供的 vm 镜像似乎并没安装 objcopy,需要使用如下语句来安装 $ cargo install cargo-binutils $ rustup component add llvm-tools-preview 否则可能出如下的错误 Failed to execute tool: objcopy No such file or directory (error 2)

bswaterb commented 2 years ago

回翻了一下开篇,发现原来是自己漏掉工具链安装那一步了 ORZ

river-art commented 2 years ago

请问 这里的 0x100c: ld t0,24(t0) 。 ld是什么意思?

bswaterb commented 2 years ago

请问 这里的 0x100c: ld t0,24(t0) 。 ld是什么意思?

代表是 load 指令

river-art commented 2 years ago

那请问 load指令 是指 汇编指令吗?

river-art commented 2 years ago

请问 ld t0,24(t0) 这个是什么意思呢?

lu-yidan commented 2 years ago
请问一下为什么在终端输入cargo build后会遇到这种问题呢,把main.rs中的use core::arch::global_asm换成use core::global_asm还是不行,下面的github链接有没有找到方案 error[E0658]: use of unstable library feature 'global_asm': global_asm! is not stable enough for use and is subject to change --> src/main.rs:8:1 8 global_asm!(include_str!("entry.asm")); ^^^^^^^^^^

= note: see issue #35119 https://github.com/rust-lang/rust/issues/35119 for more information

error[E0432]: unresolved import core::arch::global_asm --> src/main.rs:7:5 7 use core::arch::global_asm; ^^^^^^^^^^^^^^^^^^^^^^ no global_asm in arch

= note: this could be because a macro annotated with #[macro_export] will be exported at the root of the crate instead of the module where it is defined help: a macro with this name exists at the root of the crate | 7 | use core::global_asm; | ^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0432, E0658. For more information about an error, try rustc --explain E0432. error: could not compile os

To learn more, run the command again with --verbose.

wyfcyx commented 2 years ago

@Lu-Yidan 请将Rust升级到最新版本,可以在os目录下创建一个rust-toolchain文件,内容为nightly-2022-01-01

Tshiyao commented 2 years ago

虚拟磁盘文件未安装riscv64-unknown-elf-gdb,该怎么安装呢?

lu-yidan commented 2 years ago

关于执行riscv64-unknown-elf-gdb后

riscv64-unknown-elf-gdb: command not found

gdb工具链下载链接已在开发环境配置中,我将其下载解压在目录:

/home/oslab/.riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/ 具体目录根据你的情况而定

对于gdb这样一个可执行二进制文件,如何将其路径添加直环境变量中呢?

 终端中输入

gedit .bashrc

添加一行

export PATH=$PATH:/home/oslab/.riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/bin

大功告成,这样就可以直接使用该命令啦

------------------ 原始邮件 ------------------ 发件人: @.>; 发送时间: 2022年1月16日(星期天) 晚上8:05 收件人: @.>; 抄送: @.>; @.>; 主题: Re: [rcore-os/rCore-Tutorial-Book-v3] rCore-Tutorial-Book-v3/chapter1/4first-instruction-in-kernel2 (Issue #96)

虚拟磁盘文件未安装riscv64-unknown-elf-gdb,该怎么安装呢?

— Reply to this email directly, view it on GitHub, or unsubscribe. Triage notifications on the go with GitHub Mobile for iOS or Android. You are receiving this because you were mentioned.Message ID: @.***>

ZENOTME commented 2 years ago

关于内核的加载这里有一个疑问,如果说按照这里内核是直接进行从文件中进行拷贝过去,那么是否会出现文件中相应片段大小小于实际在内存中占用大小的情况?(因为传统elf文件是有可能出现片段在文件中占用大小小于实际内存中大小的)

wyfcyx commented 2 years ago

@ZENOTME 据我所知,这种情况一般只出现在零初始化的.bss段,在ELF中可能有元数据记录该段的位置而不会真的有一个全零的数据段。可以看到我们在链接脚本中将.bss段置于最后,在加载的时候并不会拷贝一个全零数据段,而在内核中我们会通过clear_bss将其清零。

konpoe commented 2 years ago

根据文档riscv-spec-20191213.pdf中Ch2和Ch25 0x1000: auipc t0,0x0 Add Upper Immediate PC: rd = pc + imm[31:12] U型指令 把当前指令的地址加上立即数这里的立即数对应的是寄存器中的高20位,相对于imm<<12,然后保存到寄存器t0 0x1004: addi a2,t0,40 ADD Immeiate: rd = rs1 + imm[11:0] 执行后 a2 = 0x1028 I型指令 0x1008: csrr a0,mhartid 伪指令 Control State Register Read: a0 = mhartid mhartid寄存器,当前hart的id#0 0x100c: ld a1,32(t0) Load Double-word: rd = rs1 + imm[11:0] I型号指令 从地址rs1+imm[11:0]出加载4个字节的数据到寄存器rd $a1 = [4bytes@0x1020] 0x1010: ld t0,24(t0) $t0 = [4bytes@1018] 这里是1018出开始加载4个字节,小端内存序,所以寄存器中的数据应该是0x0080_0000 0x1014: jr t0 伪指令,jr rs <=> jalr x0, 0(rs),Jump register 意思是跳转到t0位置

注意这里寄存器的名字, 1.开头的t代表temporary,一般用于临时变量,t0~6 2.开头的a代表argument,表示是用于函数调用传入的参数,a0~7 3这里用到了a0,a1,a2,a0是hartid,a1和a2代表了个啥不是很确定 jr应该像个函数调用传的参数是hartid : a0,unknown: a1,unkown : a2, 4.没有用到堆栈,目测是8个以内参数和7个以内临时变量的函数吧 汇编我不熟,有错误请指正

wyfcyx commented 2 years ago

@konpoe 很棒!大体上正确,帮你修改了一下格式。有一个问题是0x1010的指令执行完毕后t0应该是0x8000_0000而不是0x0080_0000,这个在前面讲解Qemu启动流程的时候有提到过。可能是小端序理解有误,从Qemu上可以看到以0x101a开头两字节的数据是0x8000,按照字节分开看,0x101a0x101b的数据分别是0x000x80。然后0x10180x1019的数据都是0x00。这样ld后的结果就是0x8000_0000

konpoe commented 2 years ago

谢谢指正

konpoe commented 2 years ago

我仔细检查了一下,ld加载双字(8字节),所以$a1 应该是[8bytes@0x1020]后面的t0应该是[8bytes@0x1018]。
应该是这8个字节的数据: 0x1018: unimp 0x101a: 0x8000 0x101c: unimp 0x101e: unimp 按照之前的教程,t0的值应该是0x0000_0000_8000_0000, 小端序是没问题的,就是gdb显示的问题。其他的好像问题不大。 我马虎过头了。

SenkiTK commented 2 years ago

弱弱问一下,为什么我使用objcopy strip之后的文件为什么会是0size呢……Orz? 错误信息如下:

llvm-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/os -O binary target/riscv64gc-unknown-none-elf/release/os.bin

stat /home/senki/Documents/xv6-rust/os/target/riscv64gc-unknown-none-elf/release/os.bin
File: /home/senki/Documents/xv6-rust/os/target/riscv64gc-unknown-none-elf/release/os.bin Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 8,3 Inode: 17961165 Links: 1 Access: (0755/-rwxr-xr-x) Uid: ( 1000/ senki) Gid: ( 1000/ senki) Access: 2022-01-30 00:08:19.840070679 +0800 Modify: 2022-01-30 00:08:19.130054408 +0800 Change: 2022-01-30 00:08:19.130054408 +0800 Birth: 2022-01-30 00:08:19.130054408 +0800

我使用rust-objcopy无法运行,所以使用了llvm-objcopy(虽然说cargo-binutils也只是对这些工具做了封装)。 所有的依赖项也是安装好了的。

SenkiTK commented 2 years ago

但是生成的os文件 大小是正常的(1000左右)

SenkiTK commented 2 years ago

我搞清楚了!原来是: stext = .; 11 .text : { 12 (.text.entry) 13 (.text .text.*) 14 } 15 16 . = ALIGN(4K); 17 etext = .;

12行与13行没有缩进(我以为不缩进功能也是一样的) 想问一下,这里是为什么需要缩进呢?

MrZLeo commented 2 years ago

演示代码

$ (gdb) si
0x0000000000001004 in ?? ()
$ (gdb) si
0x0000000000001008 in ?? ()
$ (gdb) si
0x000000000000100c in ?? ()
$ (gdb) si
0x0000000000001010 in ?? ()
$ (gdb) p/x $t0
$1 = 0x80000000
$ (gdb) si
0x0000000080000000 in ?? ()

应该是五次si后才能得到

$ (gdb) p/x $t0
$1 = 0x80000000

实机演示:

(gdb) x/10i $pc
=> 0x1000:  auipc   t0,0x0
   0x1004:  addi    a2,t0,40
   0x1008:  csrr    a0,mhartid
   0x100c:  ld  a1,32(t0)
   0x1010:  ld  t0,24(t0)
   0x1014:  jr  t0
   0x1018:  unimp
   0x101a:  0x8000
   0x101c:  unimp
   0x101e:  unimp
(gdb) si
0x0000000000001004 in ?? ()
(gdb) 
0x0000000000001008 in ?? ()
(gdb) 
0x000000000000100c in ?? ()
(gdb) 
0x0000000000001010 in ?? ()
(gdb) p/x $t0
$1 = 0x1000
(gdb) si
0x0000000000001014 in ?? ()
(gdb) p/x $t0
$2 = 0x80000000
zhangzhaoliuqun commented 2 years ago

Mac OS 执行 riscv64-unknown-elf-gdb 命令报:

riscv64-unknown-elf-gdb: command not found

解决办法:

  1. 安装 homebrew 2.执行 sudo xcode-select --install 3.执行 brew tap riscv/riscv 3.执行 brew install riscv-tools
iruhh commented 2 years ago

@Lu-Yidan 的方法有用,不过得注意: 1、gedit .bashrc的前要先cd ~让终端回到home位置。 2、export PATH=$PATH:/home/oslab/.riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14/bin可能在文件浏览器中不会显示.../oslab/...但实际是要加上的。 3、添加export PATH=$PATH:/home/osla...x-ubuntu14/bin时,要添加在最后一行。 4、编辑完.bashrc并保存后,需要重启terminal,才会生效。

wuyuesong commented 2 years ago

关于riscv64-unknown-elf-gdb,对于我而言除了@Lu-Yidan 的方法,还要对riscv64-unknown-elf-gcc-8.3.0-2020.04.1-x86_64-linux-ubuntu14 chmod -R 777 一下

brisa7444 commented 2 years ago

麻烦请问启动 Qemu 并加载 RustSBI 和内核镜像出现以下报错可能是什么原因呢?

qemu-system-riscv64 \

-machine virt \ -nographic \ -bios ../bootloader/rustsbi-qemu.bin \ -device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \ -s -S qemu-system-riscv64: Unable to load the RISC-V firmware "../bootloader/rustsbi-qemu.bin"

wuyuesong commented 2 years ago

麻烦请问启动 Qemu 并加载 RustSBI 和内核镜像出现以下报错可能是什么原因呢?

qemu-system-riscv64 \

-machine virt -nographic -bios ../bootloader/rustsbi-qemu.bin -device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 -s -S qemu-system-riscv64: Unable to load the RISC-V firmware "../bootloader/rustsbi-qemu.bin"

可能是目录下没有bootloader文件,可以从ch1分支的代码中复制一份

kaixinbaba commented 2 years ago

ch1 ..为什么我没看到有 ch1 的分支。。

kaixinbaba commented 2 years ago

原来我down错仓库了。。把 python 文档的那个仓库下载了。。。抱歉,找到了

kaixinbaba commented 2 years ago

stat target/riscv64gc-unknown-none-elf/release/os File: target/riscv64gc-unknown-none-elf/release/os Size: 1016 Blocks: 8 IO Block: 4096 regular file

为什么我的是 Size: 5240,而课件里的是 Size: 1016...


$ stat target/riscv64gc-unknown-none-elf/release/os
File: target/riscv64gc-unknown-none-elf/release/os
Size: 5240            Blocks: 16         IO Block: 4096   regular file
Device: a5h/165d        Inode: 1187295     Links: 2
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-03-28 13:02:28.913213000 +0000
Modify: 2022-03-27 15:00:03.103959000 +0000
Change: 2022-03-27 15:00:03.112959000 +0000
Birth: -
qi-xmu commented 2 years ago

wsl2环境遇到riscv64-unknown-elf-gdb缺失问题,可以按如下方法解决,具体思路是下载与编译工具链,并添加到环境变量中。

# 下载预编译文件
wget https://static.dev.sifive.com/dev-tools/freedom-tools/v2020.12/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz

后面是解压和添加环境变量,最后需要source ~/.bashrc

jiangshengdev commented 2 years ago

汇编伪指令文档: https://ftp.gnu.org/old-gnu/Manuals/gas-2.9.1/html_chapter/as_7.html

链接脚本文档: https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_chapter/ld_3.html

新版本文档: https://www.gnu.org/software/binutils/ https://sourceware.org/binutils/docs-2.38/

iLoner121 commented 2 years ago

漏掉环境配置的某些步骤,导致折腾了好久好久......

zhuiYeah commented 2 years ago

he@ubuntu:~/C_TEST/os$ qemu-system-riscv64 -machine virt -nographic -bios ../bootloader/rustsbi-qemu.bin -device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 -s -S qemu-system-riscv64: Unable to load the RISC-V firmware "../bootloader/rustsbi-qemu.bin" 这个怎么解

JasonkayZK commented 2 years ago

演示代码

$ (gdb) si
0x0000000000001004 in ?? ()
$ (gdb) si
0x0000000000001008 in ?? ()
$ (gdb) si
0x000000000000100c in ?? ()
$ (gdb) si
0x0000000000001010 in ?? ()
$ (gdb) p/x $t0
$1 = 0x80000000
$ (gdb) si
0x0000000080000000 in ?? ()

应该是五次si后才能得到

$ (gdb) p/x $t0
$1 = 0x80000000

实机演示:

(gdb) x/10i $pc
=> 0x1000:    auipc   t0,0x0
   0x1004:    addi    a2,t0,40
   0x1008:    csrr    a0,mhartid
   0x100c:    ld  a1,32(t0)
   0x1010:    ld  t0,24(t0)
   0x1014:    jr  t0
   0x1018:    unimp
   0x101a:    0x8000
   0x101c:    unimp
   0x101e:    unimp
(gdb) si
0x0000000000001004 in ?? ()
(gdb) 
0x0000000000001008 in ?? ()
(gdb) 
0x000000000000100c in ?? ()
(gdb) 
0x0000000000001010 in ?? ()
(gdb) p/x $t0
$1 = 0x1000
(gdb) si
0x0000000000001014 in ?? ()
(gdb) p/x $t0
$2 = 0x80000000

我也是这个结果

livingshade commented 2 years ago
.text : {
        *(.text.entry)
        *(.text .text.*)
    }

请问这样为什么不会让 .text.entry 被加入两次呢? 我觉得下面的 .text.text.*也能匹配 .text.entry 吧?

xamofb-xsk commented 2 years ago

请问为什么实际运行的是这样的

(gdb) x/10i $pc
=> 0x80000000:  csrw    mie,zero
   0x80000004:  csrr    a0,mhartid
   0x80000008:  auipc   sp,0xb
   0x8000000c:  addi    sp,sp,-1496
   0x80000010:  lui     t0,0x4
   0x80000012:  addi    t1,a0,1
   0x80000016:  add     sp,sp,t0
   0x80000018:  addi    t1,t1,-1
   0x8000001a:  bnez    t1,0x80000016
   0x8000001e:  auipc   ra,0x2
wyfcyx commented 2 years ago

@xamofb-xsk 因为仓库中的RustSBI版本相比文档写作时已有更新。

wyfcyx commented 2 years ago

@livingshade 段只会被插入到它在链接脚本中首次被匹配到的位置。

Goforitzjl commented 2 years ago

演示代码

$ (gdb) si 0x0000000000001004 in ?? () $ (gdb) si 0x0000000000001008 in ?? () $ (gdb) si 0x000000000000100c in ?? () $ (gdb) si 0x0000000000001010 in ?? () $ (gdb) p/x $t0 $1 = 0x80000000 $ (gdb) si 0x0000000080000000 in ?? () 应该是五次si后才能得到

$ (gdb) p/x $t0 $1 = 0x80000000 实机演示:

(gdb) x/10i $pc => 0x1000: auipc t0,0x0 0x1004: addi a2,t0,40 0x1008: csrr a0,mhartid 0x100c: ld a1,32(t0) 0x1010: ld t0,24(t0) 0x1014: jr t0 0x1018: unimp 0x101a: 0x8000 0x101c: unimp 0x101e: unimp (gdb) si 0x0000000000001004 in ?? () (gdb) 0x0000000000001008 in ?? () (gdb) 0x000000000000100c in ?? () (gdb) 0x0000000000001010 in ?? () (gdb) p/x $t0 $1 = 0x1000 (gdb) si 0x0000000000001014 in ?? () (gdb) p/x $t0 $2 = 0x80000000 我也是这个结果

CherryYang05 commented 2 years ago

macOS 上的 stat 命令可以加上 -x 参数,这样显示的内容更清晰易读。

CherryYang05 commented 2 years ago

@livingshade df

CelestialMelody commented 2 years ago
"每个段里面都包括了所有输入目标文件的同名段,
且每个段都有两个全局符号给出了它的开始和结束地址
(比如 .text 段的开始和结束地址分别是 stext 和 etext )"

Q: 在链接脚本中声明的段位全局符号?这个符号有什么用?

# os/src/entry.asm
     .section .text.entry
     .globl _start
 _start:
     li x1, 100

Q: 汇编生成的目标文件中会保留符号吗?所以链接脚本可以找到这些符号?

"冒号前面表示最终生成的可执行文件的一个段的名字,
花括号内按照放置顺序描述将所有输入目标文件的哪些段放在这个段中,
每一行格式为 <ObjectFile>(SectionName),
表示目标文件 ObjectFile 的名为 SectionName 的段需要被放进去。
我们也可以使用通配符来书写 <ObjectFile> 和 <SectionName> 
分别表示可能的输入目标文件和段名。"

...
sdata = .;
.data : {
    *(.data .data.*)
    *(.sdata .sdata.*)
}
...
.bss : {
    *(.bss.stack)
    sbss = .;
    *(.bss .bss.*)
    *(.sbss .sbss.*)
}   

Question

  1. sdata = .; 这里是声明一个段叫sdata吗?
  2. .data :{...} 外部声明 sdata = .的意义是什么? 在 .bss :{...} 内部声明 sbss = . 的意义是什么? 如果是仅仅声明这个符号,那么看起来没有什么区别;
  3. .bss.stack 是在哪里声明的? 搜索了.asm, .S,文件,均没有看见,难道是来自efi文件?
  4. *(.data .data.*) *(.sdata .sdata.*)
    1. 参考前面的引用文本,第一个*表示所有目标文件吗?
    2. 目标文件是指.o文件吗?
    3. *(.sdata .sdata.*)是什么,.sdata 不是我们声明的符号吗?难道说目标文件中存在 .sdata 以及.sdata.xxx的符号对应的段吗?
    4. 为什么 *(.sdata .sdata.*) 放在 *(.data .data.*)之后呢?这句话是什么意思?

问题太多了,不太理解这部分🥺🥺

Karthus77 commented 2 years ago

Q:我在执行gdb调试断点调试时无法到达断点0x80200000? 发现在0x0000000080004394处程序进入死循环 按照原理篇所言该处位于第二阶段,为RustSBI的初始化操作

死循环代码如下 0x0000000080004394 in ?? () ret (gdb) 0x0000000080001b14 in ?? () andi a0,a0,255 (gdb) 0x0000000080001b18 in ?? () blez a0,0x80001b00 (gdb) 0x0000000080001b00 in ?? () fence w,unknown (gdb) 0x0000000080001b04 in ?? () lb a0,8(s1) (gdb) 0x0000000080001b08 in ?? () fence r,rw (gdb) 0x0000000080001b0c in ?? () auipc ra,0x3 (gdb) 0x0000000080001b10 in ?? () jalr -1912(ra) (gdb) 0x0000000080004394 in ?? () ret

Karthus77 commented 2 years ago

Q:我在执行gdb调试断点调试时无法到达断点0x80200000? 发现在0x0000000080004394处程序进入死循环 按照原理篇所言该处位于第二阶段,为RustSBI的初始化操作 问题已经解决,镜像文件中的qemu为5.0.0版本 更新为7.0.0版本后问题解决

0warning0error commented 2 years ago

坑总结:

dynamic:从上一级 Boot Stage 获取下一级 Boot Stage 的入口信息,以 struct fw_dynamic_info 结构体通过 a2 寄存器传递。 jump:假设下一级 Boot Stage Entry 为固定地址,直接跳转过去运行。 payload:在 jump 的基础上,直接打包进来下一级 Boot Stage 的 Binary。


从https://github.com/riscv-software-src/opensbi/releases/ 下载包找到jump版本的OpenSBI并使用,即可解决
shugen002 commented 1 year ago

我想问一下,目前QEMU 7.0.0 似乎已经支持加载ELF文件作为内核的样子,我测试了一下,rust-objcopy前后的文件都可以正常完成后续的基于 SBI 服务完成输出和关机这一步,那么这里手动加载内核可执行文件中对于QEMU的表述是否应该予以修改

wyfcyx commented 1 year ago

@shugen002 多谢,后面有空的话会尝试一下。

dwr2001 commented 1 year ago

为什么cargo build时配置了linker.ld后还需要再objcopy --strip呢,我用nm查看到_start已经在需要的0x0000000080200000处了,为何要多此一举呢?

wtsclwq commented 1 year ago

@dwr2001 nm只是查看文件里的符号吧,可以符号理解为一个常量?其他指令使用这个_start符号的时候会替换成0x80200000,如果不删除元数据,那么把elf文件加载到qemu内存里之后,文件里的指令访问0x80200000这个内存的时候,对应的却不是li x1,100这条指令。

wyfcyx commented 1 year ago

@shugen002 尝试了一下,Qemu7.0似乎的确能直接加载ELF,我会尝试补充一下文档。