Lab_1:運動は、関数呼び出しスタックトレース機能(復刻版)を実現5--

トピック:関数呼び出しのスタックトレース機能を実装

私たちは、LAB1に機能print_stackframeで実装kdebug.cを完了する必要があり、機能print_stackframeによって記録された関数呼び出しスタックのリターンアドレスを追跡することが可能です。この機能が正しく実装されている場合、QEMUシミュレータと同様の出力を得るLAB1の「メークQEMU」を行ってもよいです。

1 EBP:0x00007b28 EIP:0x00100992引数:0x00010094  0x00010094  0x00007b58  0x00100096 
2カーン/デバッグ/ kdebug.c:305:print_stackframe + 22 
3 EBP:0x00007b38 EIP:0x00100c79引数:0x00000000の 0x00000000の 0x00000000の 0x00007ba8 
4カーン/デバッグ/ kmonitor.c:125: mon_backtrace + 10 
5 EBP:0x00007b58 EIP:0x00100096引数:0x00000000の 0x00007b80  を0xFFFF0000  0x00007b84
6カーニング/ INIT / init.c:48:grade_backtrace2 + 33 
7 EBP:0x00007b78 EIP:0x001000bf引数:0x00000000の を0xFFFF0000  0x00007ba4  0x00000029 
8カーン/ INIT / init.c:53:grade_backtrace1 + 38 
9 EBP:0x00007b98 EIP:0x001000dd引数:0x00000000の 0x00100000  0xFFFF0000  0x0000001d 
10カーン/ INIT / init.c:58:grade_backtrace0 + 23 
11 EBP:0x00007bb8 EIP:0x00100102引数:0x0010353c  0x00103520  0x00001308  0x00000000の
12カーン/ INIT / init.c:63:grade_backtrace + 34 
13 EBP:0x00007be8 EIP:0x00100059引数:0x00000000の 0x00000000の 0x00000000の 0x00007c53 
14カーン/ INIT / init.c:28:kern_init + 88 
15 EBP:0x00007bf8 EIP:0x00007d73引数:0xc031fcfa  0xc08ed88e  0x64e4d08e  0xfa7502a8 
16 <unknow>: - 0x00007d72 -

出力は、実質的にディスプレイと一致するかどうかを確認し、最後の行のそれぞれの値の意味を解釈するための実験を完了してください。

ヒント:あなたは、コールをどのように機能するかの関係を構築するためのコンパイラについては、「機能・スタック」を読むことができます。コンパイルLAB1を完了した後、LAB1 / OBJ / bootblock.asmを表示し、ブートローダのソースコードとマシンコードおよびステートメントのアドレスとの対応を理解し、LAB1 / OBJ / kernel.asm参照、理解し、アドレスOSのソースコードとマシンコードucore声明およびその他の対応。

機能を完了するために必要なカーニングデバッグkdebug.c :: print_stackframe実装/ /、改善されたソースパッケージ(コンパイルが行われ)、および試験報告書に実行されるプロセスの簡単な説明を、提出し、上記の質問に対する答えを書きます。

補足:
シンボリックデバッグカーネルファイルを解決するために、完全なスタック構造のニーズを表示することにより、より複雑かつ面倒。コードは、いくつかの補助機能を使用することができる持っています。例えば、print_debuginfo関数を呼び出して、画面に関数を印刷することにより、対応する関数名を見つけるために行うことができます。特にノートkdebug.cコードを参照してください。

答え

  コードの実装

    入力時にターミナル:1.は、プログラミングの前に、まず現在の状況を理解しmake qemu、次の情報を印刷した後、出口を見つけます:

1に沿って:〜/ srcに/ ucore / labcodes / LAB1 $ sudoをメイクQEMU
 2 WARNING:イメージ・フォーマットが指定されていなかったため ' ビン/ ucore.img ' 生推測プロービング。
図3は、           自動フォーマットを検出すること、危険なため、ブロックの書き込み操作、原画像0が制限されます。
4           指定の制限を削除するには、明示的にフォーマットを。
5(THU.CST)OSがある読み込んでいます...
 6つの 
7  :特別カーネルシンボル
 8    エントリ  0x00100000 (物理層)
 9    etext   0x001036f3 (物理層)
 10    EDATA   0x0010e950 (物理層)
 110x0010fdc0 (物理層)
 12カーネル実行可能なメモリフットプリント:64キロバイト

 

  関数呼び出し関係print_stackframe 2.分析

1 kern_init ->
2     grade_backtrace ->
3         grade_backtrace0(0, (int)kern_init, 0xffff0000) ->
4                 grade_backtrace1(0, 0xffff0000) ->
5                     grade_backtrace2(0, (int)&0, 0xffff0000, (int)&(0xffff0000)) ->
6                         mon_backtrace(0, NULL, NULL) ->
7                             print_stackframe ->                         

 

       3.找到print_stackframe函数,发现函数里面的注释已经提供了十分详细的步骤,基本上按照提示来做就行了。代码如下所示。

  • 首先定义两个局部变量ebp、esp分别存放ebp、esp寄存器的值。这里将ebp定义为指针,是为了方便后面取ebp寄存器的值。
  • 调用read_ebp函数来获取执行print_stackframe函数时ebp寄存器的值,这里read_ebp必须定义为inline函数,否则获取的是执行read_ebp函数时的ebp寄存器的值。
  • 调用read_eip函数来获取当前指令的位置,也就是此时eip寄存器的值。这里read_eip必须定义为常规函数而不是inline函数,因为这样的话在调用read_eip时会把当前指令的下一条指令的地址(也就是eip寄存器的值)压栈,那么在进入read_eip函数内部后便可以从栈中获取到调用前eip寄存器的值。
  • 由于变量eip存放的是下一条指令的地址,因此将变量eip的值减去1,得到的指令地址就属于当前指令的范围了。由于只要输入的地址属于当前指令的起始和结束位置之间,print_debuginfo都能搜索到当前指令,因此这里减去1即可。
  • 以后变量eip的值就不能再调用read_eip来获取了(每次调用获取的值都是相同的),而应该从ebp寄存器指向栈中的位置再往上一个单位中获取。
  • 由于ebp寄存器指向栈中的位置存放的是调用者的ebp寄存器的值,据此可以继续顺藤摸瓜,不断回溯,直到ebp寄存器的值变为0

 

 1 void print_stackframe(void) {
 2  uint32_t *ebp = 0;
 3  uint32_t esp = 0;
 4 
 5  ebp = (uint32_t *)read_ebp();
 6  esp = read_eip();
 7 
 8  while (ebp)
 9  {
10      cprintf("ebp:0x%08x eip:0x%08x args:", (uint32_t)ebp, esp);
11      cprintf("0x%08x 0x%08x 0x%08x 0x%08x\n", ebp[2], ebp[3], ebp[4], ebp[5]);
12 
13      print_debuginfo(esp - 1);
14 
15      esp = ebp[1];
16      ebp = (uint32_t *)*ebp;
17  }
18   /* LAB1 YOUR CODE : STEP 1 */
19   /* (1) call read_ebp() to get the value of ebp. the type is (uint32_t);
20    * (2) call read_eip() to get the value of eip. the type is (uint32_t);
21    * (3) from 0 .. STACKFRAME_DEPTH
22    *    (3.1) printf value of ebp, eip
23    *    (3.2) (uint32_t)calling arguments [0..4] = the contents in address (uint32_t)ebp +2 [0..4]
24    *    (3.3) cprintf("\n");
25    *    (3.4) call print_debuginfo(eip-1) to print the C calling function name and line number, etc.
26    *    (3.5) popup a calling stackframe
27    *           NOTICE: the calling funciton's return addr eip  = ss:[ebp+4]
28    *                   the calling funciton's ebp = ss:[ebp]
29    */
30 }

 

 

        4.编码完成后,执行make qemu,打印结果如下所示,与实验指导书的结果类似。

 

 1 ebp:0x00007b38 eip:0x00100bf2 args:0x00010094 0x0010e950 0x00007b68 0x001000a2
 2     kern/debug/kdebug.c:297: print_stackframe+48
 3 ebp:0x00007b48 eip:0x00100f40 args:0x00000000 0x00000000 0x00000000 0x0010008d
 4     kern/debug/kmonitor.c:125: mon_backtrace+23
 5 ebp:0x00007b68 eip:0x001000a2 args:0x00000000 0x00007b90 0xffff0000 0x00007b94
 6     kern/init/init.c:48: grade_backtrace2+32
 7 ebp:0x00007b88 eip:0x001000d1 args:0x00000000 0xffff0000 0x00007bb4 0x001000e5
 8     kern/init/init.c:53: grade_backtrace1+37
 9 ebp:0x00007ba8 eip:0x001000f8 args:0x00000000 0x00100000 0xffff0000 0x00100109
10     kern/init/init.c:58: grade_backtrace0+29
11 ebp:0x00007bc8 eip:0x00100124 args:0x00000000 0x00000000 0x00000000 0x0010379c
12     kern/init/init.c:63: grade_backtrace+37
13 ebp:0x00007be8 eip:0x00100066 args:0x00000000 0x00000000 0x00000000 0x00007c4f
14     kern/init/init.c:28: kern_init+101
15 ebp:0x00007bf8 eip:0x00007d6e args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8
16     <unknow>: -- 0x00007d6d --

  解释最后一行各个参数的含义

最后一行是ebp:0x00007bf8 eip:0x00007d6e args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8,共有ebp,eip和args三类参数,下面分别给出解释。

  1. ebp:0x0007bf8 此时ebp的值是kern_init函数的栈顶地址,从obj/bootblock.asm文件中知道整个栈的栈顶地址为0x00007c00,ebp指向的栈位置存放调用者的ebp寄存器的值,ebp+4指向的栈位置存放返回地址的值,这意味着kern_init函数的调用者(也就是bootmain函数)没有传递任何输入参数给它!因为单是存放旧的ebp、返回地址已经占用8字节了。

  2. eip:0x00007d6e eip的值是kern_init函数的返回地址,也就是bootmain函数调用kern_init对应的指令的下一条指令的地址。这与obj/bootblock.asm是相符合的。
    7d6c:   ff d0                   call   *%eax
    7d6e:   ba 00 8a ff ff          mov    $0xffff8a00,%edx

args:0xc031fcfa 0xc08ed88e 0x64e4d08e 0xfa7502a8 一般来说,args存放的4个dword是对应4个输入参数的值。但这里比较特殊,由于bootmain函数调用kern_init并没传递任何输入参数,并且栈顶的位置恰好在boot loader第一条指令存放的地址的上面,而args恰好是kern_int的ebp寄存器指向的栈顶往上第2~5个单元,因此args存放的就是boot loader指令的前16个字节!可以对比obj/bootblock.asm文件来验证(验证时要注意系统是小端字节序)。

00007c00 <start>:
    7c00:   fa                      cli    
    7c01:   fc                      cld    
    7c02:   31 c0                   xor    %eax,%eax
    7c04:   8e d8                   mov    %eax,%ds
    7c06:   8e c0                   mov    %eax,%es
    7c08:   8e d0                   mov    %eax,%ss
    7c0a:   e4 64                   in     $0x64,%al
    7c0c:   a8 02                   test   $0x2,%al
    7c0e:   75 fa                   jne    7c0a <seta20.1>

 


 

这题实在做不来,见到有位大哥做得不错,直接转载了,原文链接如下:

https://www.cnblogs.com/wuhualong/p/ucore_lab1_exercise5_report.html

 

おすすめ

転載: www.cnblogs.com/cyx-b/p/11831714.html