[rCore学习笔记 011]第1章作业题
编程题
第一题
在homework
文件夹下创建homework-1-1
,使用cargo
创建工程:
cargo new getFileName
在src
下创建file_name.rs
文件:
// /homework-1-1/getFileName/src/file_name.rs
use std::fs;
pub fn get_file_name(path:String) {
let paths = fs::read_dir(path).unwrap();
for path in paths {
println!("Name: {}", path.unwrap().path().display());
}
}
使用这个函数,编辑main.rs
:
mod file_name;
use file_name::get_file_name;
fn main() {
println!("Hello, world!");
get_file_name("./".to_string());
}
第二题
这里需要配置# libunwind
这个库.
sudo apt-get install libunwind-dev
编写main.c
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
// Compile with -funwind-tables -lunwind
void print_stack_trace_libunwind() {
printf("=== Stack trace from libunwind ===\n");
unw_cursor_t cursor; unw_context_t uc;
unw_word_t pc, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
while (unw_step(&cursor) > 0) {
unw_get_reg(&cursor, UNW_REG_IP, &pc);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
printf("Program counter: 0x%016" PRIxPTR "\n", (uintptr_t) pc);
printf("Stack pointer: 0x%016" PRIxPTR "\n", (uintptr_t) sp);
printf("\n");
}
printf("=== End ===\n\n");
}
int main()
{
print_stack_trace_libunwind();
return 0;
}
编译:
gcc -o main main.c -funwind-tables -lunwind
第三题
这道题建议 学完第三章 之后再进行编程,这时候对时间的理解会比较好.
学完第三章之后我们自己的工程就已经被修改地 面目全非 了.
这时候选择在rCore-Tutorial-v3
的基础上进行修改运行.
cd ~/App/rCore-Tutorial-v3
git checkout ch1
这里注意如果报错error: Your local changes to the following files would be overwritten by checkout
,可以使用git checkout -- .
取消掉所有的更改.这样就可以切换回ch1
分支了.
这时候需要实现一个timer
模块来实现延时就好了.
这里需要引入riscv
模块,编辑os/Cargo.toml
.
[dependencies]
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
编写延迟模块:
// os/src/timer.rs
use riscv::register::time;
use crate::config::CLOCK_FREQ;
const MICRO_PER_SEC: usize = 1_000_000;
pub fn sleep_us(us: usize) {
let time = time::read();
while time::read() - time < us*(CLOCK_FREQ / MICRO_PER_SEC) {}
}
这里可以看到时钟大小是来源于config
模块:
// os/src/config.rs
pub const CLOCK_FREQ: usize = 10000000;
CLOCK_FREQ
的值怎么获得呢?
需要通过如下指令获取dump.dts
文件:
qemu-system-riscv64 -machine virt,dumpdtb=dump.dtb
dtc -o dump.dts dump.dtb
dump.dts
文件里写了timebase-frequency = <0x989680>;
,十六进制转换为十进制因此 时基频率 为 10000000.
接下来只需要在main.rs
里调用即可.
...
use timer::sleep_us;
...
...
mod config;
mod timer;
...
#[no_mangle]
pub fn rust_main() -> ! {
...
sleep_us(5000000);
println!("Weakup! 5s is too looooong!");
...
sbi::shutdown(false)
}
随后直接在os
文件夹下make run
,从打印出的log不能反映具体情况,但是执行的时候会明显感受到延时了5s
:
[rustsbi] RustSBI version 0.3.1, adapting to RISC-V SBI v1.0.0
.______ __ __ _______.___________. _______..______ __
| _ \ | | | | / | | / || _ \ | |
| |_) | | | | | | (----`---| |----`| (----`| |_) || |
| / | | | | \ \ | | \ \ | _ < | |
| |\ \----.| `--' |.----) | | | .----) | | |_) || |
| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|
[rustsbi] Implementation : RustSBI-QEMU Version 0.2.0-alpha.2
[rustsbi] Platform Name : riscv-virtio,qemu
[rustsbi] Platform SMP : 1
[rustsbi] Platform Memory : 0x80000000..0x88000000
[rustsbi] Boot HART : 0
[rustsbi] Device Tree Region : 0x87000000..0x87000f02
[rustsbi] Firmware Address : 0x80000000
[rustsbi] Supervisor Address : 0x80200000
[rustsbi] pmp01: 0x00000000..0x80000000 (-wr)
[rustsbi] pmp02: 0x80000000..0x80200000 (---)
[rustsbi] pmp03: 0x80200000..0x88000000 (xwr)
[rustsbi] pmp04: 0x88000000..0x00000000 (-wr)
[kernel] Hello, world!
Weakup! 5s is too looooong!
问答题
第一题
- 说明我们需要了解计算机的资源,而使用操作系统来调配计算机的资源是非常重要的事情.
应用程序在执行过程中会占用CPU资源(CPU 流水线,缓存等)和内存.
这里主要需要关注的就是CPU的流水线和缓存的概念,之前脑子里并没有了解过
detail
CPU 流水线 (Pipeline)
CPU流水线是一种设计技术,用于提高处理器执行指令的速度。它通过将指令执行过程分解成多个阶段,并允许每个阶段同时处理不同的指令,从而实现并行处理的效果。这种并行不是指同时执行多条指令,而是指在同一个时钟周期内,CPU的不同部分可以处理不同指令的不同步骤。
流水线的基本阶段:
- 取指(Fetch):从内存中读取下一条要执行的指令。
- 解码(Decode):分析指令,确定需要执行的操作以及操作数的位置。
- 执行(Execute):执行指令的操作,可能涉及到算术逻辑单元(ALU)、加载/存储数据到寄存器或内存。
- 访存(Memory):如果指令需要访问内存(如加载或存储操作),则在此阶段完成。
- 写回(Write Back):将执行结果写回到寄存器或内存中。
流水线的工作原理就像工厂的装配线,每个阶段专注于一项特定任务,使得整个过程更加高效。理想情况下,每过一个时钟周期,CPU就能完成一条指令的执行,即使是在复杂的多阶段流水线中。
缓存 (Cache)
缓存是一种高速存储器,用于暂时存储处理器最频繁访问的数据和指令的副本。其目的是减少从主内存获取数据所需的时间,因为主内存的访问速度远低于处理器的运行速度。缓存利用了程序的局部性原理,即程序倾向于重复使用最近使用过的数据或指令。
缓存的层次:
- L1 Cache(一级缓存):位于CPU内部,速度最快但容量最小。
- L2 Cache(二级缓存):通常也集成在CPU上,比L1大但慢一点。
- L3 Cache(三级缓存):可能在CPU内部也可能在主板上,容量更大,速度较慢,但在多核处理器中共享,用于减少不同核心间的延迟。
当CPU需要数据时,它首先检查L1缓存,然后是L2缓存,最后是L3缓存,如果在任何缓存级别中找到数据,则立即使用;如果没有找到,则从主内存中读取,并将数据复制到缓存中,以便后续更快地访问。
缓存的管理机制包括替换策略(如LRU,最近最少使用)和一致性协议(如MESI),以确保数据的正确性和效率。缓存命中率(数据在缓存中找到的频率)是衡量缓存效率的关键指标。高命中率意味着大部分数据请求都可以快速满足,从而显著提升性能。
第二题
- 需要学会使用
readelf
等调试工具
直接分析homework-0-3
生成的程序文件:
readelf -a main
生成的log
如下所示:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Position-Independent Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x10c0
Start of program headers: 64 (bytes into file)
Start of section headers: 14104 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 30
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000318 00000318
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.gnu.pr[...] NOTE 0000000000000338 00000338
0000000000000030 0000000000000000 A 0 0 8
[ 3] .note.gnu.bu[...] NOTE 0000000000000368 00000368
0000000000000024 0000000000000000 A 0 0 4
[ 4] .note.ABI-tag NOTE 000000000000038c 0000038c
0000000000000020 0000000000000000 A 0 0 4
[ 5] .gnu.hash GNU_HASH 00000000000003b0 000003b0
0000000000000024 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 00000000000003d8 000003d8
00000000000000f0 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 00000000000004c8 000004c8
00000000000000a1 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 000000000000056a 0000056a
0000000000000014 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000000580 00000580
0000000000000030 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 00000000000005b0 000005b0
00000000000000c0 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000000670 00000670
0000000000000060 0000000000000018 AI 6 24 8
[12] .init PROGBITS 0000000000001000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000001020 00001020
0000000000000050 0000000000000010 AX 0 0 16
[14] .plt.got PROGBITS 0000000000001070 00001070
0000000000000010 0000000000000010 AX 0 0 16
[15] .plt.sec PROGBITS 0000000000001080 00001080
0000000000000040 0000000000000010 AX 0 0 16
[16] .text PROGBITS 00000000000010c0 000010c0
000000000000015c 0000000000000000 AX 0 0 16
[17] .fini PROGBITS 000000000000121c 0000121c
000000000000000d 0000000000000000 AX 0 0 4
[18] .rodata PROGBITS 0000000000002000 00002000
000000000000003a 0000000000000000 A 0 0 8
[19] .eh_frame_hdr PROGBITS 000000000000203c 0000203c
0000000000000034 0000000000000000 A 0 0 4
[20] .eh_frame PROGBITS 0000000000002070 00002070
00000000000000ac 0000000000000000 A 0 0 8
[21] .init_array INIT_ARRAY 0000000000003da0 00002da0
0000000000000008 0000000000000008 WA 0 0 8
[22] .fini_array FINI_ARRAY 0000000000003da8 00002da8
0000000000000008 0000000000000008 WA 0 0 8
[23] .dynamic DYNAMIC 0000000000003db0 00002db0
00000000000001f0 0000000000000010 WA 7 0 8
[24] .got PROGBITS 0000000000003fa0 00002fa0
0000000000000060 0000000000000008 WA 0 0 8
[25] .data PROGBITS 0000000000004000 00003000
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 0000000000004010 00003010
0000000000000008 0000000000000000 WA 0 0 1
[27] .comment PROGBITS 0000000000000000 00003010
000000000000002b 0000000000000001 MS 0 0 1
[28] .symtab SYMTAB 0000000000000000 00003040
00000000000003a8 0000000000000018 29 18 8
[29] .strtab STRTAB 0000000000000000 000033e8
0000000000000212 0000000000000000 0 0 1
[30] .shstrtab STRTAB 0000000000000000 000035fa
000000000000011a 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000002d8 0x00000000000002d8 R 0x8
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000006d0 0x00000000000006d0 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000229 0x0000000000000229 R E 0x1000
LOAD 0x0000000000002000 0x0000000000002000 0x0000000000002000
0x000000000000011c 0x000000000000011c R 0x1000
LOAD 0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
0x0000000000000270 0x0000000000000278 RW 0x1000
DYNAMIC 0x0000000000002db0 0x0000000000003db0 0x0000000000003db0
0x00000000000001f0 0x00000000000001f0 RW 0x8
NOTE 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000030 0x0000000000000030 R 0x8
NOTE 0x0000000000000368 0x0000000000000368 0x0000000000000368
0x0000000000000044 0x0000000000000044 R 0x4
GNU_PROPERTY 0x0000000000000338 0x0000000000000338 0x0000000000000338
0x0000000000000030 0x0000000000000030 R 0x8
GNU_EH_FRAME 0x000000000000203c 0x000000000000203c 0x000000000000203c
0x0000000000000034 0x0000000000000034 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000002da0 0x0000000000003da0 0x0000000000003da0
0x0000000000000260 0x0000000000000260 R 0x1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .plt.sec .text .fini
04 .rodata .eh_frame_hdr .eh_frame
05 .init_array .fini_array .dynamic .got .data .bss
06 .dynamic
07 .note.gnu.property
08 .note.gnu.build-id .note.ABI-tag
09 .note.gnu.property
10 .eh_frame_hdr
11
12 .init_array .fini_array .dynamic .got
Dynamic section at offset 0x2db0 contains 27 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x1000
0x000000000000000d (FINI) 0x121c
0x0000000000000019 (INIT_ARRAY) 0x3da0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x3da8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x3b0
0x0000000000000005 (STRTAB) 0x4c8
0x0000000000000006 (SYMTAB) 0x3d8
0x000000000000000a (STRSZ) 161 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x3fa0
0x0000000000000002 (PLTRELSZ) 96 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x670
0x0000000000000007 (RELA) 0x5b0
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000000000001e (FLAGS) BIND_NOW
0x000000006ffffffb (FLAGS_1) Flags: NOW PIE
0x000000006ffffffe (VERNEED) 0x580
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x56a
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0
Relocation section '.rela.dyn' at offset 0x5b0 contains 8 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000003da0 000000000008 R_X86_64_RELATIVE 11a0
000000003da8 000000000008 R_X86_64_RELATIVE 1160
000000004008 000000000008 R_X86_64_RELATIVE 4008
000000003fd8 000100000006 R_X86_64_GLOB_DAT 0000000000000000 __libc_start_main@GLIBC_2.34 + 0
000000003fe0 000200000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_deregisterTM[...] + 0
000000003fe8 000500000006 R_X86_64_GLOB_DAT 0000000000000000 __gmon_start__ + 0
000000003ff0 000700000006 R_X86_64_GLOB_DAT 0000000000000000 _ITM_registerTMCl[...] + 0
000000003ff8 000900000006 R_X86_64_GLOB_DAT 0000000000000000 __cxa_finalize@GLIBC_2.2.5 + 0
Relocation section '.rela.plt' at offset 0x670 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000003fb8 000300000007 R_X86_64_JUMP_SLO 0000000000000000 printf@GLIBC_2.2.5 + 0
000000003fc0 000400000007 R_X86_64_JUMP_SLO 0000000000000000 fputs@GLIBC_2.2.5 + 0
000000003fc8 000600000007 R_X86_64_JUMP_SLO 0000000000000000 fopen@GLIBC_2.2.5 + 0
000000003fd0 000800000007 R_X86_64_JUMP_SLO 0000000000000000 sleep@GLIBC_2.2.5 + 0
No processor specific unwind information to decode
Symbol table '.dynsym' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (2)
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [...]@GLIBC_2.2.5 (3)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.2.5 (3)
5: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@GLIBC_2.2.5 (3)
7: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5 (3)
9: 0000000000000000 0 FUNC WEAK DEFAULT UND [...]@GLIBC_2.2.5 (3)
Symbol table '.symtab' contains 39 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS Scrt1.o
2: 000000000000038c 32 OBJECT LOCAL DEFAULT 4 __abi_tag
3: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
4: 00000000000010f0 0 FUNC LOCAL DEFAULT 16 deregister_tm_clones
5: 0000000000001120 0 FUNC LOCAL DEFAULT 16 register_tm_clones
6: 0000000000001160 0 FUNC LOCAL DEFAULT 16 __do_global_dtors_aux
7: 0000000000004010 1 OBJECT LOCAL DEFAULT 26 completed.0
8: 0000000000003da8 0 OBJECT LOCAL DEFAULT 22 __do_global_dtor[...]
9: 00000000000011a0 0 FUNC LOCAL DEFAULT 16 frame_dummy
10: 0000000000003da0 0 OBJECT LOCAL DEFAULT 21 __frame_dummy_in[...]
11: 0000000000000000 0 FILE LOCAL DEFAULT ABS main.c
12: 0000000000000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
13: 0000000000002118 0 OBJECT LOCAL DEFAULT 20 __FRAME_END__
14: 0000000000000000 0 FILE LOCAL DEFAULT ABS
15: 0000000000003db0 0 OBJECT LOCAL DEFAULT 23 _DYNAMIC
16: 000000000000203c 0 NOTYPE LOCAL DEFAULT 19 __GNU_EH_FRAME_HDR
17: 0000000000003fa0 0 OBJECT LOCAL DEFAULT 24 _GLOBAL_OFFSET_TABLE_
18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_mai[...]
19: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
20: 0000000000004000 0 NOTYPE WEAK DEFAULT 25 data_start
21: 0000000000004010 0 NOTYPE GLOBAL DEFAULT 25 _edata
22: 000000000000121c 0 FUNC GLOBAL HIDDEN 17 _fini
23: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5
24: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.2.5
25: 0000000000004000 0 NOTYPE GLOBAL DEFAULT 25 __data_start
26: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
27: 0000000000004008 0 OBJECT GLOBAL HIDDEN 25 __dso_handle
28: 0000000000002000 4 OBJECT GLOBAL DEFAULT 18 _IO_stdin_used
29: 0000000000004018 0 NOTYPE GLOBAL DEFAULT 26 _end
30: 00000000000010c0 38 FUNC GLOBAL DEFAULT 16 _start
31: 0000000000004010 0 NOTYPE GLOBAL DEFAULT 26 __bss_start
32: 00000000000011a9 115 FUNC GLOBAL DEFAULT 16 main
33: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fopen@GLIBC_2.2.5
34: 0000000000004010 0 OBJECT GLOBAL HIDDEN 25 __TMC_END__
35: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_registerTMC[...]
36: 0000000000000000 0 FUNC GLOBAL DEFAULT UND sleep@GLIBC_2.2.5
37: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize@G[...]
38: 0000000000001000 0 FUNC GLOBAL HIDDEN 12 _init
Histogram for `.gnu.hash' bucket list length (total of 2 buckets):
Length Number % of total Coverage
0 1 ( 50.0%)
1 1 ( 50.0%) 100.0%
Version symbols section '.gnu.version' contains 10 entries:
Addr: 0x000000000000056a Offset: 0x00056a Link: 6 (.dynsym)
000: 0 (*local*) 2 (GLIBC_2.34) 1 (*global*) 3 (GLIBC_2.2.5)
004: 3 (GLIBC_2.2.5) 1 (*global*) 3 (GLIBC_2.2.5) 1 (*global*)
008: 3 (GLIBC_2.2.5) 3 (GLIBC_2.2.5)
Version needs section '.gnu.version_r' contains 1 entry:
Addr: 0x0000000000000580 Offset: 0x000580 Link: 7 (.dynstr)
000000: Version: 1 File: libc.so.6 Cnt: 2
0x0010: Name: GLIBC_2.2.5 Flags: none Version: 3
0x0020: Name: GLIBC_2.34 Flags: none Version: 2
Displaying notes found in: .note.gnu.property
Owner Data size Description
GNU 0x00000020 NT_GNU_PROPERTY_TYPE_0
Properties: x86 feature: IBT, SHSTK
x86 ISA needed: x86-64-baseline
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 8ba5fa4303543a75b0a426d6d1bf0125d4dc4fb5
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
我们可以搜到:
[16] .text PROGBITS 00000000000010c0 000010c0
000000000000015c 0000000000000000 AX 0 0 16
[25] .data PROGBITS 0000000000004000 00003000
0000000000000010 0000000000000000 WA 0 0 8
detail
以.text
为例:
- [16]:这通常是段的索引号,表明这是ELF文件中的第16个段。ELF文件可能包含多个段,每个段有各自的作用,如
.text
、.data
、.rodata
等。 - .text:这是段的名称,
.text
段通常存放程序的机器代码。 - PROGBITS:这是段的类型,
PROGBITS
表示这是一个可读写的程序数据段。对于.text
段,尽管标记为PROGBITS
,但实际上它是只读的,用于存储机器指令。 - 00000000000010c0 000010c0:第一个数值是段在文件中的偏移量,即从文件头开始到段起始点的距离。在这个例子中,
.text
段从文件偏移量0x10c0
处开始。第二个数值是段在虚拟内存中的地址,.text
段在内存中的起始地址是0x10c0
。 - 000000000000015c:这是段的大小,即
.text
段占用的字节数。在这个例子中,.text
段的大小是0x15c
字节,即348字节。 - 0000000000000000:这是段的链接信息,通常为0,表示没有特别的链接属性。
- AX:这是段的标志,
A
表示段是可分配的(allocatable),X
表示段是可执行的(executable)。这意味着.text
段会被加载到内存中,并且其中的代码可以被执行。 - 0:这是段的对齐要求的指数部分,这里为0,表示对齐要求为
2^0
字节,即无特殊对齐要求。但在实践中,.text
段通常会有更高的对齐要求,如4字节或8字节对齐,以满足CPU的性能要求。 - 0:这是段的压缩类型,0表示未压缩。
在ELF文件格式的段标志中,常见的标志包括但不限于:
R
:表示段是可读的(Readable)。W
:表示段是可写的(Writable)。X
:表示段是可执行的(Executable)。A
:表示段是可分配的(Allocatable)。
第三题
操作系统是一种特殊的应用程序,它直接和硬件驱动层交互,使得完成一些资源的分配,其余的应用程序是使用的操作系统的接口.
第四题
^038649
在 QEMU 源码 1 中可以找到“上电”的时候刚执行的几条指令,如下:
uint32_t reset_vec[10] = {
0x00000297, /* 1: auipc t0, %pcrel_hi(fw_dyn) */
0x02828613, /* addi a2, t0, %pcrel_lo(1b) */
0xf1402573, /* csrr a0, mhartid */
#if defined(TARGET_RISCV32)
0x0202a583, /* lw a1, 32(t0) */
0x0182a283, /* lw t0, 24(t0) */
#elif defined(TARGET_RISCV64)
0x0202b583, /* ld a1, 32(t0) */
0x0182b283, /* ld t0, 24(t0) */
#endif
0x00028067, /* jr t0 */
start_addr, /* start: .dword */
start_addr_hi32,
fdt_load_addr, /* fdt_laddr: .dword */
0x00000000,
/* fw_dyn: */
};
完成的工作是: ^67887c
- 读取当前的 Hart ID CSR
mhartid
写入寄存器a0
- (我们还没有用到:将 FDT (Flatten device tree) 在物理内存中的地址写入
a1
) - 跳转到
start_addr
,在我们实验中是 RustSBI 的地址
第五题
Supervisor Binary Interface Specification
detail
"Supervisor Binary Interface Specification"(SBI,监督器二进制接口规范)是RISC-V架构中定义的一个重要规范,用于描述处理器内核与其监督器(通常是一个运行在更高特权级的固件或操作系统)之间的通信协议。SBI提供了内核与监督器之间进行交互的一组标准化的二进制接口,使得内核能够调用监督器提供的服务,如系统调用、设备驱动初始化、中断处理等,而无需直接访问硬件或了解硬件的详细布局。
SBI规范的设计目标是提高软件的可移植性和硬件的抽象程度,同时简化内核的开发和维护。通过SBI,内核开发者可以编写与硬件无关的代码,而监督器负责实现硬件特定的细节,这样可以更容易地在不同的RISC-V硬件平台上移植操作系统和固件。
SBI规范定义了一系列的函数调用,每个函数都有一个特定的编号,内核通过向监督器发送这个编号和相关的参数来调用特定的服务。监督器根据编号识别出内核的请求,并执行相应的操作,完成后返回结果给内核。这种设计允许内核和监督器之间有一个清晰的边界,同时也保证了内核的安全性和稳定性。
总之,"Supervisor Binary Interface Specification"是RISC-V生态系统中的一项关键规范,它为内核和监督器之间的通信建立了一套统一的标准,极大地促进了RISC-V软件生态的发展和硬件的兼容性。
第六题
编译器依赖操作系统提供的程序库,操作系统执行应用程序需要编译器提供段位置、符号表、依赖库等信息。 ELF 就是比较常见的一种文件格式。
第七题
接第 4 题,跳转到 RustSBI 后,SBI 会对部分硬件例如串口等进行初始化,然后通过 mret 跳转到 payload 也就是 kernel 所在的起始地址。kernel 进行一系列的初始化后(内存管理,虚存管理,线程(进程)初始化等),通过 sret 跳转到应用程序的第一条指令开始执行。
第八题
应用程度对内存的访问需要通过 MMU 的地址翻译完成,应用程序运行时看到的地址和实际位于内存中的地址是不同的,栈空间和地址空间需要内核进行管理和分配。应用程序的栈指针在 trap return 过程中初始化。此外,应用程序可能需要动态加载某些库的内容,也需要内核完成映射。
第九题
这道题参看了答案,感觉答案的那个互相调用的函数写得非常之巧妙,认真学习.
这道题在学习的过程中反复参看反复开始看不懂然后跳着看,实际上是大脑罢工了,这时候我们就应该从容易理解的入手,比如先理解提到的pc
, sp
, ra
三个寄存器的作用:
- 程序计数器(pc)
pc
寄存器存储着下一条要执行的指令的地址。每当一条指令执行完毕,pc
通常会被更新为指向接下来的指令地址。在RISC-V中,由于指令长度固定为32位,pc
通常只需简单地增加4(即32位的字节数)即可指向下一指令。- 当遇到分支、跳转或调用指令时,
pc
的值可能会被更新为指令中指定的新地址。
- 栈指针(sp)
sp
寄存器指向当前栈顶的地址。栈是一种先进后出(FILO)的数据结构,用于存储函数调用期间的局部变量、参数以及保存的寄存器值。- 在RISC-V中,栈通常以向下增长的方式使用(即向更低的地址扩展)。当函数调用开始时,
sp
会先减去所需的栈空间大小,然后在栈中分配局部变量和保存寄存器值;当函数调用结束时,sp
会相应地增加,释放这部分栈空间。
- 返回地址(ra)
ra
寄存器用于保存函数调用返回地址,即调用者函数中紧跟在函数调用指令之后的指令地址。当一个函数执行完毕并返回时,处理器会从ra
中获取返回地址,从而继续执行调用者函数中的后续指令。- 在RISC-V中,
jal
(JAL)指令用于函数调用,它会自动将返回地址保存到ra
中,而jalr
指令则允许通过寄存器间接获取返回地址。
这里参看答案很久还是搞不定的原因是对于函数调用的规范还是不熟悉,需要重新参看一下文档.
答案是这么写的:
- 首先,我们当前的 pc
在 flip
函数的开头,这是我们正在运行的函数。返回给调用者处的地址在 ra
寄存器里,是 0x10742
。因为我们还没有开始操作栈指针,所以调用处的 sp
与我们相同,都是 0x40007f1310
。
- 0x10742
在 flap
函数内。根据 flap
函数的开头可知,这个函数的栈帧大小是 16 个字节,所以调用者处的栈指针应该是 sp + 16 = 0x40007f1320
。调用 flap
的调用者返回地址保存在栈上 8(sp)
,可以读出来是 0x10750
,还在 flap
函数内。
- 依次类推,只要能理解已知地址对应的函数代码,就可以完成恢复操作。
我再理清自己的思路复述一遍,
pc
的位置为什么和sp
指针的指向有关,因为在函数入口会开辟一个新的栈帧,即[[09 为内核支持函数调用#^c6691b|让栈指针的值从高位向低位变化]].然后会保存当前ra
和sp
指针到刚才的栈帧中,方法是 正偏移 一个量,这样就可在更高的地址(栈帧内部)储存信息.因为此时pc
还没有进入flip
函数,因此此时的sp
在上一个栈帧的结尾,也就是调用flip
函数的位置.此时sp
的值为0x40007f1310
,查看函数flip
,把ra
存在sp
偏移了8
的位置中 .此时读取ra
的值为0x10742 <flap+18>
,即在flap
函数内部偏移18
的地方调用了flip
函数.- 这里可以得出
flap
->flip
flip
的sp
为0x40007f1310
- 这里可以得出
- 因为
flap
函数的栈帧为16
字节,因此可以看出调用者的栈指针为sp + 16 = 0x40007f1320
,查看函数flap
,把ra
存在sp
偏移了8
的位置中 的操作.因此我们总是可以通过读取sp
的偏移8
位的值知道调用位置在哪,以知道是谁调用了当前分析到的函数,然后根据栈帧大小得出上一个sp
位置.- 这里可以得出
flap
->flip
flap
的sp
为0x40007f1320
- 这里可以得出
ld ra,8(sp)
,可以求出上次的调用的地址存在8(sp)
内容为0x10750
(这里文档中没有提供),还在flap
函数内,又知道flap
的栈帧大小为16
- 这里可以得出
flap
->flap
->flip
- 第一个
flap
的sp
为0x40007f1330
- 这里可以得出
- 刚好和
x/6a $sp
得到的信息是相符合的, 只不过那几个调用地址是需要我们自己读8(sp)
的,而不是用x/6a
看答案得到的.