Pre Merge pull request !2 from xzcxzcyy/master

pull/2/MERGE
xzcxzcyy 3 years ago committed by Gitee
commit 57b3660719

@ -9,6 +9,7 @@ Copyright (c) 2021, Zhiyuan Shao (zyshao@hust.edu.cn),
Ziming Yuan (1223962053@qq.com), Ziming Yuan (1223962053@qq.com),
Yixin Song (yixinsong@hust.edu.cn), Yixin Song (yixinsong@hust.edu.cn),
Boyang Li (liboyang_hust@163.com), Boyang Li (liboyang_hust@163.com),
Zichen Xu (xuzichen@hust.edu.cn)
Huazhong University of Science and Technology Huazhong University of Science and Technology
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining

@ -1638,7 +1638,7 @@ System is shutting down with exit code 0.
```bash ```bash
//切换到lab1_challenge1_backtrace //切换到lab1_challenge1_backtrace
$ git checkout lab1_challenge1_backtrace $ git checkout lab1_challenge1_backtrace
//继承lab1_3以及之前的答案 //继承lab1_3以及之前的答案
$ git merge lab1_3_irq -m "continue to work on lab1_challenge1" $ git merge lab1_3_irq -m "continue to work on lab1_challenge1"
@ -1654,12 +1654,6 @@ $ git merge lab1_3_irq -m "continue to work on lab1_challenge1"
应用程序调用print_backtrace()时应能够通过控制输入的参数如例子user/app_print_backtrace.c中的7控制回溯的层数。例如如果调用print_backtrace(5)则只输出5层回溯如果调用print_backtrace(100)则应只回溯到main函数就停止回溯因为调用的深度小于100 应用程序调用print_backtrace()时应能够通过控制输入的参数如例子user/app_print_backtrace.c中的7控制回溯的层数。例如如果调用print_backtrace(5)则只输出5层回溯如果调用print_backtrace(100)则应只回溯到main函数就停止回溯因为调用的深度小于100
- 讨论的简化
实验可以对中间函数如user/app_print_backtrace.c中的f1到f8进行讨论上的简化可假设它们都不带参数。
<a name="lab1_challenge1_guide"></a> <a name="lab1_challenge1_guide"></a>
#### 实验指导 #### 实验指导
@ -1668,8 +1662,26 @@ $ git merge lab1_3_irq -m "continue to work on lab1_challenge1"
- 系统调用路径上的完善,可参见[3.2](#syscall)中的知识; - 系统调用路径上的完善,可参见[3.2](#syscall)中的知识;
- 在操作系统内核中获取用户程序的栈。这里需要注意的是PKE系统中当用户程序通过系统调用陷入到内核时会切换到S模式的“用户内核”栈而不是在用户栈上继续操作。我们的print_backtrace()函数的设计目标是回溯并打印用户进程的函数调用情况,所以,进入操作系统内核后,需要找到用户进程的用户态栈来开始回溯; - 在操作系统内核中获取用户程序的栈。这里需要注意的是PKE系统中当用户程序通过系统调用陷入到内核时会切换到S模式的“用户内核”栈而不是在用户栈上继续操作。我们的print_backtrace()函数的设计目标是回溯并打印用户进程的函数调用情况,所以,进入操作系统内核后,需要找到用户进程的用户态栈来开始回溯;
- 找到用户态栈后,我们需要了解用户态栈的结构。实际上,这一点在我们的第一章就有[举例](chapter1_riscv.md#call_stack_structure)来说明,读者可以回顾一下第一章的例子。另外,由于我们可以对讨论进行简化(即假设中间函数无参数),那么单次函数调用的栈深度我们也可以进行相应的假设; - 找到用户态栈后,我们需要了解用户态栈的结构。实际上,这一点在我们的第一章就有[举例](chapter1_riscv.md#call_stack_structure)来说明,读者可以回顾一下第一章的例子;
- 通过用户栈找到函数的返回地址后需要将虚拟地址转换为源程序中的符号。这一点读者需要了解ELF文件中的符号节.symtab section以及字符串节.strtab section的相关知识了解这两个节section里存储的内容以及存储的格式等内容。对ELF的这两个节网上有大量的介绍例如[这里](https://blog.csdn.net/edonlii/article/details/8779075)。 - 通过用户栈找到函数的返回地址后需要将虚拟地址转换为源程序中的符号。这一点读者需要了解ELF文件中的符号节.symtab section以及字符串节.strtab section的相关知识了解这两个节section里存储的内容以及存储的格式等内容。对ELF的这两个节网上有大量的介绍例如[这里](https://blog.csdn.net/edonlii/article/details/8779075),或阅读[Linux Man Page](https://man7.org/linux/man-pages/man5/elf.5.html)。
提示:
理论上我们希望你了解函数调用时栈帧的结构通过fp寄存器(s0)寻找各个栈帧然而编译器一般会优化掉fp使得它的值始终为0在发生函数调用时也不保存这个寄存器实验的Makefile已经使用`-fno-omit-frame-pointer`禁止了此项优化。此时函数调用的栈帧类似下图:
<img src="pictures/fig1_2.png" alt="fig1_2" style="zoom:100%;" />
注意函数调用的叶子结点一般不会将ra保存到栈中
如果你发现使用fp追踪栈底难度太大可以假设用户程序的函数调用产生的栈帧总是定长的为了获得这个长度你可以
```bash
$ riscv64-unknown-elf-objdump -d obj/app_print_backtrace
```
观察`f1`~ `f8`开始时的汇编代码,特别注意用户态函数`do_user_call`,它的栈帧与`f1` 等略有不同。
使用这种方法虽然在局部变量太多,或者函数参数较多时无法正确实现 backtrace 功能,也不是我们预期的做法,但我们进行测试时确实会使用简单的测试用例(没有参数,局部变量),因此可以通过 :)
**注意完成实验内容后请读者另外编写应用通过调用print_backtrace()函数,并带入不同的深度参数,对自己的实现进行检测。** **注意完成实验内容后请读者另外编写应用通过调用print_backtrace()函数,并带入不同的深度参数,对自己的实现进行检测。**

Loading…
Cancel
Save