当前位置:网站首页 > R语言数据分析 > 正文

detr源码(devmem源码)



调试Linux kernel源码要分两部分,分别是MMU开启之前与MMU开启之后,这是因为在没有打开MMU之前,CPU直接访问物理内存,而一旦MMU开启,CPU对memory系统的访问需要通过一系列的Translation table进行翻译,即访问的是虚拟地址空间。在MMU开启之前,内核代码是位置无关的代码(Position Independent Code, PIC),可以在任意地址上运行,也就导致了运行地址与链接地址不一致的情况,需要加载symbol到相应的位置才能进行debug。而在MMU开启之后,内核开始运行在虚拟地址上,此时运行地址和链接地址是一致的。

 01 确定kernel的加载地址

要调试kernel需要知道kernel被OpenSBI加载到何处,即需要知道kernel的运行地址,这里说的是物理地址,可以分析OpenSBI源码得到,或者还有一个更简单的方法,就是修改OpenSBI源码,直接将kernel的运行地址打印出来

这里我们找到sbi_hart_switch_mode函数:

void __attribute__((noreturn))

sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,

           unsigned long next_addr, unsigned long next_mode,

           bool next_virt)

{

#if __riscv_xlen == 32

    unsigned long val, valH;

#else

    unsigned long val;

#endif

    switch (next_mode) {

    case PRV_M:

        break;

    case PRV_S:

        if (!misa_extension('S'))

            sbi_hart_hang();

        break;

    case PRV_U:

        if (!misa_extension('U'))

            sbi_hart_hang();

        break;

    default:

        sbi_hart_hang();

    }

    val = csr_read(CSR_MSTATUS);

    val = INSERT_FIELD(val, MSTATUS_MPP, next_mode);

    val = INSERT_FIELD(val, MSTATUS_MPIE, 0);

#if __riscv_xlen == 32

    if (misa_extension('H')) {

        valH = csr_read(CSR_MSTATUSH);

        valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);

        csr_write(CSR_MSTATUSH, valH);

    }

#else

    if (misa_extension('H'))

        val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);

#endif

    csr_write(CSR_MSTATUS, val);

    csr_write(CSR_MEPC, next_addr);

    if (next_mode == PRV_S) {

        if (next_virt) {

            csr_write(CSR_VSTVEC, next_addr);

            csr_write(CSR_VSSCRATCH, 0);

            csr_write(CSR_VSIE, 0);

            csr_write(CSR_VSATP, 0);

        } else {

            csr_write(CSR_STVEC, next_addr);

            csr_write(CSR_SSCRATCH, 0);

            csr_write(CSR_SIE, 0);

            csr_write(CSR_SATP, 0);

        }

    } else if (next_mode == PRV_U) {

        if (misa_extension('N')) {

            csr_write(CSR_UTVEC, next_addr);

            csr_write(CSR_USCRATCH, 0);

            csr_write(CSR_UIE, 0);

        }

    }

    register unsigned long a0 asm("a0") = arg0;

    register unsigned long a1 asm("a1") = arg1;

    __asm__ __volatile__("mret" : : "r"(a0), "r"(a1));

    __builtin_unreachable();

}

该函数就是用来从OpenSBI跳转到kernel执行的一段代码,因为从OpenSBI跳转到kernel执行需要从Machine模式切换到 Supervisor模式,所以需要mret指令,而mret指令执行时,会将CSR_MEPC的值复制到PC中,也就是说CSR_MEPC中存放了mret指令返回之后需要执行的地址,而CSR_MEPC的值就是next_addr,也就是kernel的加载地址,我们把next_addr打印出来就可以确定kernel的地址了,关于代码的详解,后面我会专门写文章进行分析,现在我们来加一句打印,如下图:

前面加一串1是为了醒目一点,也便于在输出的log中进行搜索,重新编译OpenSBI并运行,可以看到输出的kernel address为0x:

02 MMU开启之前的调试

知道了kernel的运行地址之后,我们就可以调试kernel了,先来调试MMU开启之前的代码,基本都是汇编代码,调试之前需要对kernel进行配置

执行如下命令:

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- menuconfig

然后进行如下配置:

Kernel hacking --->

Compile-time checks and compiler options  --->

Debug information

Rely on the toolchain's implicit default DWARF version

‍然后重新编译kernel:

make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- -j12

编译完成后运行qemu:

https://www.bilibili.com/opus/run.sh -S -s

此时qemu等待gdb连接

启动gdb之前我们还需要看一下kernel的各个段的地址,在linux源码目录下执行如下命令:

readelf -S vmlinux

得到如下段信息:

There are 40 section headers, starting at offset 0x10fd0368:

Section Headers:

 [Nr] Name       Type       Address      Offset

    Size       EntSize     Flags Link Info Align

 [ 0]          NULL       0000000000000000 00000000

    0000000000000000 0000000000000000      0   0   0

 [ 1] .head.text    PROGBITS     ffffffff 00001000

    0000000000001e9c 0000000000000000 AX    0   0   4096

 [ 2] .text       PROGBITS     ffffffff 00003000

    0000000000973fdc 0000000000000000 AX    0   0   4

 [ 3] .init.text    PROGBITS     ffffffff80a00000 00a00000

    000000000004a4a2 0000000000000000 AX    0   0   

 [ 4] .exit.text    PROGBITS     ffffffff80a4a4a8 00a4a4a8

    00000000000027a8 0000000000000000 AX    0   0   2

 [ 5] .init.data    PROGBITS     ffffffff80c00000 00a4d000

    0000000000024320 0000000000000000 WA    0   0   4096

 [ 6] .init.pi     PROGBITS     ffffffff80c24320 00a71320

    0000000000002533 0000000000000000 WAX    0   0   8

 [ 7] .init.bss     NOBITS      ffffffff80c26858 00a73853

    0000000000000048 0000000000000000 WA    0   0   8

 [ 8] .data..percpu   PROGBITS     ffffffff80c27000 00a74000

    000000000000c0a8 0000000000000000 WA    0   0   64

 [ 9] .alternative   PROGBITS     ffffffff80c330a8 00a800a8

    00000000000017c0 0000000000000000  A    0   0   1

 [10] .rodata      PROGBITS     ffffffff80e00000 00a82000

    00000000002c6ff0 0000000000000000 WA    0   0   256

 [11] .pci_fixup    PROGBITS     ffffffff810c6ff0 00d48ff0

    0000000000004068 0000000000000000  A    0   0   8

 [12] __ksymtab     PROGBITS     ffffffff810cb058 00d4d058

    000000000001dc70 0000000000000000  A    0   0   8

 [13] __ksymtab_gpl   PROGBITS     ffffffff810e8cc8 00d6acc8

    0000000000025260 0000000000000000  A    0   0   8

 [14] __ksymtab_strings PROGBITS     ffffffff8110df28 00d8ff28

    0000000000036833 0000000000000001 AMS    0   0   1

 [15] __param      PROGBITS     ffffffff 00dc6760

    0000000000003340 0000000000000000  A    0   0   8

 [16] __modver     PROGBITS     ffffffff81147aa0 00dc9aa0

    0000000000000240 0000000000000000 WA    0   0   8

 [17] __ex_table    PROGBITS     ffffffff81147ce0 00dc9ce0

    0000000000002040 0000000000000000  A    0   0   4

 [18] .notes      NOTE       ffffffff81149d20 00dcbd20

    0000000000000054 0000000000000000  A    0   0   4

 [19] .srodata     PROGBITS     ffffffff 00dcc000

    0000000000001738 0000000000000000  A    0   0   8

 [20] .data       PROGBITS     ffffffff 00dce000

    00000000000f7b80 0000000000000000 WA    0   0   4096

 [21] __bug_table    PROGBITS     ffffffff814f7b80 00ec5b80

    000000000001b378 0000000000000000 WA    0   0   1

 [22] .sdata      PROGBITS     ffffffff81512ef8 00ee0ef8

    0000000000002140 0000000000000000 WA    0   0   8

 [23] .got       PROGBITS     ffffffff 00ee3038

    0000000000000020 0000000000000008 WA    0   0   8

 [24] .pecoff_edat[...] PROGBITS     ffffffff 00ee3058

    00000000000001a8 0000000000000000  A    0   0   1

 [25] .sbss       NOBITS      ffffffff 00ee3200

    0000000000002895 0000000000000000 WA    0   0   64

 [26] .bss       NOBITS      ffffffff 00ee3200

    00000000000778f0 0000000000000000 WA    0   0   4096

 [27] .debug_aranges  PROGBITS     0000000000000000 00ee3200

    0000000000023720 0000000000000000      0   0   16

 [28] .debug_info    PROGBITS     0000000000000000 00f06920

    000000000a7aac35 0000000000000000      0   0   1

 [29] .debug_abbrev   PROGBITS     0000000000000000 0b6b1555

    00000000004ecafe 0000000000000000      0   0   1

 [30] .debug_line    PROGBITS     0000000000000000 0bb9e053

    0000000001e4ad57 0000000000000000      0   0   1

 [31] .debug_frame   PROGBITS     0000000000000000 0d9e8db0

    00000000002d8e50 0000000000000000      0   0   8

 [32] .debug_str    PROGBITS     0000000000000000 0dcc1c00

    000000000031e645 0000000000000001 MS    0   0   1

 [33] .debug_line_str  PROGBITS     0000000000000000 0dfe0245

    00000000000137bc 0000000000000001 MS    0   0   1

 [34] .debug_loclists  PROGBITS     0000000000000000 0dff3a01

    00000000020a1d35 0000000000000000      0   0   1

 [35] .debug_rnglists  PROGBITS     0000000000000000 

    000000000091383b 0000000000000000      0   0   1

 [36] .comment     PROGBITS     0000000000000000 109a8f71

    000000000000002b 0000000000000001 MS    0   0   1

 [37] .symtab      SYMTAB      0000000000000000 109a8fa0

    000000000041b7b8 0000000000000018     38     8

 [38] .strtab      STRTAB      0000000000000000 10dc4758

    000000000020ba72 0000000000000000      0   0   1

 [39] .shstrtab     STRTAB      0000000000000000 10fd01ca

    0000000000000198 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), p (processor specific)

我们需要知道如下段的地址,其他的暂时可以不用管:

.head.text ffffffff

.text ffffffff

.init.text ffffffff80a00000

.rodata ffffffff80e00000

知道这些段地址之后,就可以启动gdb了,在新终端上输入如下命令:

gdb-multiarch

然后在gdb命令行中输入如下内容加载symbol:

add-symbol-file vmlinux 0x -s .head.text 0x -s .init.text 0x80c00000 -s .rodata 0x

然后输入y,如下图:

上面symbol各个段加载的地址的计算方法是:

kernel运行地址+相对于0xffffffff的偏移

0x+(addr-0xffffffff)

然后连接qemu:

target remote:1234

之后就可以设置断点进行调试了,注意只能设置MMU开启之前的断点:

到此,MMU开启之前的代码就可以使用gdb单步进行跟踪调试了,下面我们来看MMU开启之后的调试步骤。

 03 MMU开启之后的调试

其实,MMU开启之后的调试比开启之前的调试简单很多,因为MMU开启之后,CPU访问的都是虚拟地址,而kernel链接地址就是按照虚拟地址进行的,也就是说运行地址和链接地址是一致的,这种情况下,直接按照vmlinux中的symbol进行加载即可

依然是先运行qemu:

https://www.bilibili.com/opus/run.sh -S -s

重新开启一个终端,输入如下命令:

gdb-multiarch vmlinux

之后在gdb命令行执行:

target remote:1234

此时就可以设置断点进行调试了,注意只能设置MMU开启之后的断点:

注意上图中的断点是start_kernel不是MMU开启之前的_start_kernel,不带下划线

终于熬夜写完了qemu+gdb调试系列~

到这里,qemu+gdb调试OpenSBI和kernel都已经完结了,恭喜你,你可以进行OpenSBI和Linux kernel代码的研究了,看不懂的地方不放gdb调试一下。

如果你觉得对你有帮助,请一键三连哟~

到此这篇detr源码(devmem源码)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • arrport怎么读(arr怎么读音)2025-06-23 16:00:08
  • docker的版本(dockerversion)2025-06-23 16:00:08
  • torpedo复数(to的复数怎么读)2025-06-23 16:00:08
  • emr和aes区别(emr和apc的区别)2025-06-23 16:00:08
  • 加载msvcp失败(msvcr100.dll加载失败)2025-06-23 16:00:08
  • prim算法csdn(prim算法适用于什么图)2025-06-23 16:00:08
  • swagger2配置(swagger2配置实体类不生效)2025-06-23 16:00:08
  • docker发展史(docker公司发展历程)2025-06-23 16:00:08
  • windows tree命令参数(win10 tree命令)2025-06-23 16:00:08
  • top18女rapper(top18女rapper日本)2025-06-23 16:00:08
  • 全屏图片