[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的不同部分可以处理不同指令的不同步骤。

流水线的基本阶段:
  1. 取指(Fetch):从内存中读取下一条要执行的指令。
  2. 解码(Decode):分析指令,确定需要执行的操作以及操作数的位置。
  3. 执行(Execute):执行指令的操作,可能涉及到算术逻辑单元(ALU)、加载/存储数据到寄存器或内存。
  4. 访存(Memory):如果指令需要访问内存(如加载或存储操作),则在此阶段完成。
  5. 写回(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为例:

  1. [16]:这通常是段的索引号,表明这是ELF文件中的第16个段。ELF文件可能包含多个段,每个段有各自的作用,如.text.data.rodata等。
  2. .text:这是段的名称,.text段通常存放程序的机器代码。
  3. PROGBITS:这是段的类型,PROGBITS表示这是一个可读写的程序数据段。对于.text段,尽管标记为PROGBITS,但实际上它是只读的,用于存储机器指令。
  4. 00000000000010c0 000010c0:第一个数值是段在文件中的偏移量,即从文件头开始到段起始点的距离。在这个例子中,.text段从文件偏移量0x10c0处开始。第二个数值是段在虚拟内存中的地址,.text段在内存中的起始地址是0x10c0
  5. 000000000000015c:这是段的大小,即.text段占用的字节数。在这个例子中,.text段的大小是0x15c字节,即348字节。
  6. 0000000000000000:这是段的链接信息,通常为0,表示没有特别的链接属性。
  7. AX:这是段的标志,A表示段是可分配的(allocatable),X表示段是可执行的(executable)。这意味着.text段会被加载到内存中,并且其中的代码可以被执行。
  8. 0:这是段的对齐要求的指数部分,这里为0,表示对齐要求为2^0字节,即无特殊对齐要求。但在实践中,.text段通常会有更高的对齐要求,如4字节或8字节对齐,以满足CPU的性能要求。
  9. 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 过程中初始化。此外,应用程序可能需要动态加载某些库的内容,也需要内核完成映射。

第九题

这道题参看了答案,感觉答案的那个互相调用的函数写得非常之巧妙,认真学习.

这道题在学习的过程中反复参看反复开始看不懂然后跳着看,实际上是大脑罢工了,这时候我们就应该从容易理解的入手,比如先理解提到的pcspra三个寄存器的作用:

  1. 程序计数器(pc)
    • pc寄存器存储着下一条要执行的指令的地址。每当一条指令执行完毕,pc通常会被更新为指向接下来的指令地址。在RISC-V中,由于指令长度固定为32位,pc通常只需简单地增加4(即32位的字节数)即可指向下一指令。
    • 当遇到分支、跳转或调用指令时,pc的值可能会被更新为指令中指定的新地址。
  2. 栈指针(sp)
    • sp寄存器指向当前栈顶的地址。栈是一种先进后出(FILO)的数据结构,用于存储函数调用期间的局部变量、参数以及保存的寄存器值。
    • 在RISC-V中,栈通常以向下增长的方式使用(即向更低的地址扩展)。当函数调用开始时,sp会先减去所需的栈空间大小,然后在栈中分配局部变量和保存寄存器值;当函数调用结束时,sp会相应地增加,释放这部分栈空间。
  3. 返回地址(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 函数内。
- 依次类推,只要能理解已知地址对应的函数代码,就可以完成恢复操作。

我再理清自己的思路复述一遍,

  1. pc的位置为什么和sp指针的指向有关,因为在函数入口会开辟一个新的栈帧,即[[09 为内核支持函数调用#^c6691b|让栈指针的值从高位向低位变化]].然后会保存当前rasp指针到刚才的栈帧中,方法是 正偏移 一个量,这样就可在更高的地址(栈帧内部)储存信息.因为此时pc还没有进入flip函数,因此此时的sp在上一个栈帧的结尾,也就是调用flip函数的位置.此时sp的值为0x40007f1310,查看函数flip,把 ra存在sp偏移了8的位置中 .此时读取ra的值为0x10742 <flap+18>,即在flap函数内部偏移18 的地方调用了flip函数.
    1. 这里可以得出 flap -> flip
    2. flipsp0x40007f1310
  2. 因为flap函数的栈帧为16字节,因此可以看出调用者的栈指针为sp + 16 = 0x40007f1320,查看函数flap,把 ra存在sp偏移了8的位置中 的操作.因此我们总是可以通过读取sp的偏移8位的值知道调用位置在哪,以知道是谁调用了当前分析到的函数,然后根据栈帧大小得出上一个sp位置.
    1. 这里可以得出 flap -> flip
    2. flapsp0x40007f1320
  3. ld ra,8(sp),可以求出上次的调用的地址存在 8(sp)内容为0x10750 (这里文档中没有提供),还在 flap 函数内,又知道flap 的栈帧大小为16
    1. 这里可以得出 flap->flap -> flip
    2. 第一个flapsp0x40007f1330
  4. 刚好和x/6a $sp得到的信息是相符合的, 只不过那几个调用地址是需要我们自己读8(sp)的,而不是用x/6a看答案得到的.
posted @ 2024-07-09 19:54  winddevil  阅读(13)  评论(0编辑  收藏  举报