MIPS32的內部寄存器。


基礎知識介紹:

 

  1. MIPS32的內部寄存器。

最簡單的辦法就是通過GDB的命令,可以獲得下面的列表

(gdb) info registers

          zero       at       v0       v1       a0       a1       a2       a3

 R0   00000000 00000001 0000000f 0000000f 00000000 0000000f 0000000e 00000071

            t0       t1       t2       t3       t4       t5       t6       t7

 R8   00000072 00000001 00000203 80003cb1 80003cb0 0000007f 00000080 00000008

            s0       s1       s2       s3       s4       s5       s6       s7

 R16  00000001 80003bb0 00000000 00000000 00000000 00000000 00000000 00000000

            t8       t9       k0       k1       gp       sp       s8       ra

 R24  0000101a 0000000d 00000000 00000000 8000bbd0 807fffb8 00000000 80000830

            sr       lo       hi      bad    cause       pc

      00000000 00000000 00000007 00000000 00000000 80000830

           fsr      fir

      00000000 00000000

除了32個通用寄存器以及別名外,還有8個專用寄存器,分別是:

sr ( 全稱Status ,CP0 Reg12) Processor status and control; interrupt control; and shadow set control

lo ( 全稱WatchLo , CP0 Reg18) Low-order watchpoint address

hi ( 全稱WatchHi, CP0 Reg19) High-order watchpoint address

bad ( 全稱BadVAddr, CP0 Reg8) Reports the address for the most recent address-related exception

cause (CP0 Reg13) Cause of last exception

pc 很明顯這個是程序計數寄存器,奇怪的是在32個通用寄存器以及CP0的32個寄存器中都沒有找到他,最接近的一個是CP0Reg14 EPC (Program counter at last exception.)

fsr 浮點相關寄存器,具體用途不明

fir浮點相關寄存器,具體用途不明

 

下表描述32個通用寄存器的別名和用途

;REGISTER

NAME

USAGE

$0

$zero

常量0(constant value 0)

$1

$at

保留給匯編器(Reserved for assembler)

$2-$3

$v0-$v1

函數調用返回值(values for results and expression evaluation)

$4-$7

$a0-$a3

函數調用參數(arguments)

$8-$15

$t0-$t7

暫時的(或隨便用的)

$16-$23

$s0-$s7

保存的(或如果用,需要SAVE/RESTORE的)(saved)

$24-$25

$t8-$t9

暫時的(或隨便用的)

$28

$gp

全局指針(Global Pointer)

$29

$sp

堆棧指針(Stack Pointer)

$30

$fp

幀指針(Frame Pointer)

$31

$ra

返回地址(return address)

 

Table: MIPS registers and the convention governing their use.

Register Name

Number

Usage

zero

0

Constant 0

at

1

Reserved for assembler

v0

2

Expression evaluation and results of a function

v1

3

Expression evaluation and results of a function

a0

4

Argument 1

a1

5

Argument 2

a2

6

Argument 3

a3

7

Argument 4

t0

8

Temporary (not preserved across call)

t1

9

Temporary (not preserved across call)

t2

10

Temporary (not preserved across call)

t3

11

Temporary (not preserved across call)

t4

12

Temporary (not preserved across call)

t5

13

Temporary (not preserved across call)

t6

14

Temporary (not preserved across call)

t7

15

Temporary (not preserved across call)

s0

16

Saved temporary (preserved across call)

s1

17

Saved temporary (preserved across call)

s2

18

Saved temporary (preserved across call)

s3

19

Saved temporary (preserved across call)

s4

20

Saved temporary (preserved across call)

s5

21

Saved temporary (preserved across call)

s6

22

Saved temporary (preserved across call)

s7

23

Saved temporary (preserved across call)

t8

24

Temporary (not preserved across call)

t9

25

Temporary (not preserved across call)

k0

26

Reserved for OS kernel

k1

27

Reserved for OS kernel

gp

28

Pointer to global area

sp

29

Stack pointer

fp or s8

30

Frame pointer

ra

31

Return address (used by function call)

 

 

2.      基於Linux的環境,應用程序可以通過拋出信號的方法掛起當前的任務,操作系統會將該任務控制塊信息(TCB)交由應用程序注冊的信號處理函數來處理,該信息中包含了

下面這個信號量上下文結構體,里面含有我們需要的CPU寄存器信息。

linux/2.4.20/include/asm-mips/sigcontext.h

/*

 * Keep this struct definition in sync with the sigcontext fragment

 * in arch/mips/tools/offset.c

 */

struct sigcontext {

        unsigned int       sc_regmask;          /* Unused */

        unsigned int       sc_status;

        unsigned long long sc_pc;

        unsigned long long sc_regs[32];

        unsigned long long sc_fpregs[32];

        unsigned int       sc_ownedfp;          /* Unused */

        unsigned int       sc_fpc_csr;

        unsigned int       sc_fpc_eir;          /* Unused */

        unsigned int       sc_used_math;

        unsigned int       sc_ssflags;          /* Unused */

        unsigned long long sc_mdhi;

        unsigned long long sc_mdlo;

 

        unsigned int       sc_cause;            /* Unused */

        unsigned int       sc_badvaddr;         /* Unused */

 

        unsigned long      sc_sigset[4];        /* kernel's sigset_t */

};

 

  1. mips32常用匯編指令描述

源代碼:

#include <stdio.h>

#include <stdlib.h>

 

int func_b(int a)

{

    return 0;

}

 

int func_a(int a)

{

    func_b(0);

    return 0;

}

 

int main(int argc, char* argv[])

{

    int temp = 0;

    func_a(temp);

    return 0;

}

 

下面是將-O2編譯出的elf反編譯后func_a的匯編指令:

-   0x80000810 <func_a>:         lui a0,0x8000  /* a0 = 0x80000000 */

-   0x80000814 <func_a+4>:       lui a1,0x8000 /* a1 = 0x80000000 */

-   0x80000818 <func_a+8>:       addiu  sp,sp,-24

/* sp = sp - 24 = 0x807fffd0 - 24 = 0x807FFFB8 */

-   0x8000081c <func_a+12>:      addiu  a0,a0,12876

/* a0 = a0 + 12876 = 0x8000324C */

-   0x80000820 <func_a+16>:      addiu  a1,a1,12888

/* a1 = a1 + 12888 */

-   0x80000824 <func_a+20>:      sw  ra,16(sp)

/* SW Store Word Mem[Rs+offset] = Rt

ra = 0x80000858

sp = 0x807FFFB8

功能相當於下面的C代碼

*(unsigned int*)(sp + 16) = ra */

-   0x80000828 <func_a+24>:      jal 0x80000dd0 <printf>

/* JAL Jump and Link GPR[31] = PC + 8

PC = PC[31:28] || offset<<2

ra = pc+8 = 0x80000828 + 8 = 0x80000830

pc = 0x80000dd0 */

    0x8000082c <func_a+28>:      li  a2,78

-   0x80000830 <func_a+32>:      jal 0x800007e0 <func_b>

    0x80000834 <func_a+36>:      move   a0,zero

-   0x80000838 <func_a+40>:      lw  ra,16(sp)

-   0x8000083c <func_a+44>:      move   v0,zero

/* v0 = 0 */

-   0x80000840 <func_a+48>:      jr  ra

JR Jump Register PC = Rs

    0x80000844 <func_a+52>:      addiu  sp,sp,24

閱讀上面代碼發現一個問題:

為什么ra = pc+8而不是pc+4呢?這樣看來0x8000082c、0x80000834、0x80000844這3個地址對應的指令不會被執行到。

 

下面這段來自《MIPS32 4K Processor Core Family Software User’s Manual》

Jump and branch instructions change the control flow of a program. All jump and branch instructions occur with a delay of one instruction: that is, the instruction immediately following the jump or branch (this is known as the instruction in the delay slot) always executes while the target instruction is being fetched from storage.

簡單的說,因為MIPS的多級流水機制導致Jump和Branch指令后面的一個指令會被放在延時槽中,無條件執行。

 

 

下面是一些網站上找到的描述:

http://gcc.gnu.org/ml/gcc-help/2008-01/msg00059.html

How to traceback call stack on MIPS arch?

 

Gcc saves the frame pointer to fp(s8) register at the beginning of each function if compiling source with -O0. But  it won't do so if compiling source with -O2. Without frame pointers, can I trace back call stacks in current function context? Or is there any option which forces gcc to save frame pointers for MIPS arch?

 

PRC

2008/1/8

這個問題是關於GCC優化的,看看下面這個表就清楚了。從實際測試情況看,fp(s8)也就是通用寄存器30可以用sp也就是通用寄存器29來代替,因為在函數領空(不包含子函數調用)的時候sp是保持固定值的,因為沒有類似於x86的pop和push指令,該問題只着眼於當前函數上下文,沒有考慮到向前追溯的問題。

mips_fp_be-gcc -O0 -g test.c -o btO0

mips_fp_be-objdump -S btO0 > asmO0.txt

mips_fp_be-gcc -O2 -g test.c -o btO2

mips_fp_be-objdump -S btO2 > asmO2.txt

00400e0c <main>:

 

int

main (int argc, char **argv)

{

  400e0c: 3c1c0fc0 lui  gp,0xfc0

  400e10: 279c79b4 addiu    gp,gp,31156

  400e14: 0399e021 addu gp,gp,t9

  400e18: 27bdffd8 addiu    sp,sp,-40

  400e1c: afbc0010 sw   gp,16(sp)

  400e20: afbf0020 sw   ra,32(sp)

  400e24: afbe001c sw   s8,28(sp)

  400e28: afbc0018 sw   gp,24(sp)

  400e2c: 03a0f021 move s8,sp

  400e30: afc40028 sw   a0,40(s8)

  400e34: afc5002c sw   a1,44(s8)

  print_backtrace (); 

  400e38: 8f9980a4 lw   t9,-32604(gp)

  400e3c: 00000000 nop

  400e40: 0320f809 jalr t9

  400e44: 00000000 nop

  400e48: 8fdc0010 lw   gp,16(s8)

  return 0;

  400e4c: 00001021 move v0,zero

}

  400e50: 03c0e821 move sp,s8

  400e54: 8fbf0020 lw   ra,32(sp)

  400e58: 8fbe001c lw   s8,28(sp)

  400e5c: 03e00008 jr   ra

  400e60: 27bd0028 addiu    sp,sp,40

     ...

00400de4 <main>:

 

int

main (int argc, char **argv)

{

  400de4: 3c1c0fc0 lui  gp,0xfc0

  400de8: 279c79dc addiu    gp,gp,31196

  400dec: 0399e021 addu gp,gp,t9

  400df0: 27bdffe0 addiu    sp,sp,-32

  400df4: afbc0010 sw   gp,16(sp)

  400df8: afbf001c sw   ra,28(sp)

  400dfc: afbc0018 sw   gp,24(sp)

  print_backtrace (); 

  400e00: 8f9980a4 lw   t9,-32604(gp)

  400e04: 00000000 nop

  400e08: 0320f809 jalr t9

  400e0c: 00000000 nop

  400e10: 8fbc0010 lw   gp,16(sp)

  return 0;

}

  400e14: 8fbf001c lw   ra,28(sp)

  400e18: 00001021 move v0,zero

  400e1c: 03e00008 jr   ra

  400e20: 27bd0020 addiu    sp,sp,32

     ...

 

 

You need to use the unwinder.

 

#include <unwind.h>

#include <stdio.h>

 

static _Unwind_Reason_Code

backtrace_helper (struct _Unwind_Context *ctx, void *a)

{

  void *ip = (void*)_Unwind_GetIP (ctx);

  fprintf (stdout, "   %p/n", ip);

  return _URC_NO_REASON;

 

void

print_backtrace (void)

{

  _Unwind_Backtrace (backtrace_helper, NULL);

}

 

int

main (int argc, char **argv)

{

  print_backtrace (); 

  return 0;

}

該回答解釋了上面的問題,提出用_Unwind_Backtrace函數來顯示caller的地址,其實就MIPS而言對於單枝函數(沒有子函數調用的函數)只要讀ra寄存器的值就可以了,對於非單枝函數需要從堆棧里恢復出ra並顯示。同樣的問題這個函數也沒有做向前的追溯。

 

For that to work, you must compile all the code with -fexceptions.

 

You could also try compiling all the code with -fno-omit-framepointer and writing your own unwinder.  I posted such an unwinder to java-patches@gcc.gnu.org several years ago.  Later versions of GCC are starting to do optimizations in the function prolog that make unwinding without the unwinder meta-data very difficult.

 

David Daney

該回答給出了2個GCC的參數,也是回答了上面的問題。

 

根據MIPS寄存器定義和GCC生成的機器碼可以得到網上描述的“MIPS不支持C函數的幀結構”。

我的理解是和x86的ESP和EBP寄存器比,MIPS的確是無法直觀的從寄存器里找到當前情況下堆棧的底部,每個函數對應的棧的尺寸是由GCC計算出的,函數返回時的棧的恢復也是通過立即數的方式通過指令來實現,如(addiu    sp,sp,40),這樣我們做BackTrace最重要的一個問題就是確定每級函數的堆棧尺寸。

 

我覺得要確定每級函數的堆棧尺寸,只能通過解析機器碼來實現。湊巧發現netbsd系統在內核代碼中實現了對MIPS體系結構backtrace的支持,現在來分析下核心代碼。

代碼在:

http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/arch/mips/mips/trap.c?rev=1.217.12.21&content-type=text/x-cvsweb-markup

代碼的全路徑:

/src/sys/arch/mips/mips/trap.c

 

#define   MIPS_JR_RA          0x03e00008          /* instruction code for jr ra */
#define   MIPS_JR_K0          0x03400008          /* instruction code for jr k0 */
#define   MIPS_ERET 0x42000018          /* instruction code for eret */
 
/*
 * Do a stack backtrace.
 * (*printfn)()  prints the output to either the system log,
 * the console, or both.
 */
void
stacktrace_subr(mips_reg_t a0, mips_reg_t a1, mips_reg_t a2, mips_reg_t a3,
    vaddr_t pc, vaddr_t sp, vaddr_t fp, vaddr_t ra,
    void (*printfn)(const char*, ...))
{
          vaddr_t va, subr;
          unsigned instr, mask;
          InstFmt i;
          int more, stksize;
          unsigned int frames =  0;
          int foundframesize = 0;
#ifdef DDB
          db_expr_t diff;
          db_sym_t sym;
#endif
 
/* Jump here when done with a frame, to start a new one */
loop:
          stksize = 0;
          subr = 0;
          if (frames++ > 100) {
                    (*printfn)("/nstackframe count exceeded/n");
                    /* return breaks stackframe-size heuristics with gcc -O2 */
                    goto finish;        /*XXX*/
          }
 
          /* check for bad SP: could foul up next frame */
          if (sp & 3 || (intptr_t)sp >= 0) { /* 首先堆棧值應該4字節地址對齊,其次bit31應該為1表示內核空間*/
                    (*printfn)("SP 0x%x: not in kernel/n", sp);
                    ra = 0;
                    subr = 0;
                    goto done;
          }
 
          /* Check for bad PC */
          if (pc & 3 || (intptr_t)pc >= 0 || (intptr_t)pc >= (intptr_t)edata) {
                    (*printfn)("PC 0x%x: not in kernel space/n", pc);
                    ra = 0;
                    goto done;
          }
 
#ifdef DDB
          /*
           * Check the kernel symbol table to see the beginning of
           * the current subroutine.
           */
          diff = 0;
          sym = db_search_symbol(pc, DB_STGY_ANY, &diff);
          if (sym != DB_SYM_NULL && diff == 0) {
                    /* check func(foo) __attribute__((__noreturn__)) case */
                    instr = kdbpeek(pc - 2 * sizeof(int));
                    i.word = instr;
                    if (i.JType.op == OP_JAL) {
                               sym = db_search_symbol(pc - sizeof(int),
                                   DB_STGY_ANY, &diff);
                               if (sym != DB_SYM_NULL && diff != 0)
                                         diff += sizeof(int);
                    }
          }
          if (sym == DB_SYM_NULL) {
                    ra = 0;
                    goto done;
          }
          va = pc - diff;
#else /* 基本來到這里,我們沒有這個存放符號信息的數據庫 */
          /*
           * Find the beginning of the current subroutine by scanning backwards
           * from the current PC for the end of the previous subroutine.
           * 
           * XXX This won't work well because nowadays gcc is so aggressive
           *     as to reorder instruction blocks for branch-predict.
           *     (i.e. 'jr ra' wouldn't indicate the end of subroutine)
           */
          /* 這里向前搜索OPCODE,直到找到0x03e00008或者產生地址越界(超出了代碼段最小可能地址verylocore),這樣做是不可靠的,因為是通過搜索上一個函數的特征機器碼來確定當前函數的頂部,所以一個假設前提是前面還有函數*/
          va = pc;
          do {
                    va -= sizeof(int);
                    if (va <= (vaddr_t)verylocore)
                               goto finish;
                    instr = kdbpeek(va);
                    if (instr == MIPS_ERET)
                               goto mips3_eret;
          } while (instr != MIPS_JR_RA && instr != MIPS_JR_K0);
          /* skip back over branch & delay slot */
          va += sizeof(int); /*跳過延時槽*/
mips3_eret:
          va += sizeof(int); /*跳過JR_RA指令 */
          /* skip over nulls which might separate .o files */
          while ((instr = kdbpeek(va)) == 0)
                    va += sizeof(int); /* 跳過無用的空指令 */
#endif
          subr = va; /* 得到當前函數的首地址 */
 
          /* scan forwards to find stack size and any saved registers */
          stksize = 0;
          more = 3;
          mask = 0;
          foundframesize = 0;
          for (va = subr; more; va += sizeof(int),
                                     more = (more == 3) ? 3 : more - 1) {
                    /* stop if hit our current position */
                    if (va >= pc)
                               break;
                    instr = kdbpeek(va);
                    i.word = instr;
                    switch (i.JType.op) {
                    case OP_SPECIAL:
                               switch (i.RType.func) {
                               case OP_JR:
                               case OP_JALR:
                                         more = 2; /* stop after next instruction */
                                         break;
 
                               case OP_SYSCALL:
                               case OP_BREAK:
                                         more = 1; /* stop now */
                               };
                               break;
 
                    case OP_BCOND:
                    case OP_J:
                    case OP_JAL:
                    case OP_BEQ:
                    case OP_BNE:
                    case OP_BLEZ:
                    case OP_BGTZ:
                               more = 2; /* stop after next instruction */
                               break;
 
                    case OP_COP0:
                    case OP_COP1:
                    case OP_COP2:
                    case OP_COP3:
                               switch (i.RType.rs) {
                               case OP_BCx:
                               case OP_BCy:
                                         more = 2; /* stop after next instruction */
                               };
                               break;
 
                    case OP_SW:/* 解析存放在堆棧上的有用數據:包括4個傳遞參數的寄存器、幀指針、函數返回地址*/
#if !defined(__mips_o32)
                    case OP_SD:
#endif
                    {
                               size_t size = (i.JType.op == OP_SW) ? 4 : 8;
 
                               /* look for saved registers on the stack */
                               if (i.IType.rs != 29)
                                         break;
                               /* only restore the first one */
                               if (mask & (1 << i.IType.rt))
                                         break;
                               mask |= (1 << i.IType.rt);
                               switch (i.IType.rt) {
                               case 4: /* a0 */
                                         a0 = kdbrpeek(sp + (short)i.IType.imm, size);
                                         break;
 
                               case 5: /* a1 */
                                         a1 = kdbrpeek(sp + (short)i.IType.imm, size);
                                         break;
 
                               case 6: /* a2 */
                                         a2 = kdbrpeek(sp + (short)i.IType.imm, size);
                                         break;
 
                               case 7: /* a3 */
                                         a3 = kdbrpeek(sp + (short)i.IType.imm, size);
                                         break;
 
                               case 30: /* fp */
                                         fp = kdbrpeek(sp + (short)i.IType.imm, size);
                                         break;
 
                               case 31: /* ra */
                                         ra = kdbrpeek(sp + (short)i.IType.imm, size);
                               }
                               break;
                    }
 
                    case OP_ADDI:
                    case OP_ADDIU: /* 這里來分析堆棧的尺寸,是一個類似於addiu    sp,sp,-24的指令,我們要將立即數取出,並負負得正 */
#if !defined(__mips_o32)
                    case OP_DADDI:
                    case OP_DADDIU:
#endif
                               /* look for stack pointer adjustment */
                               if (i.IType.rs != 29 || i.IType.rt != 29)
                                         break;
                               /* don't count pops for mcount */
                               if (!foundframesize) {
                                         stksize = - ((short)i.IType.imm);
                                         foundframesize = 1;
                               }
                    }
          }
done:
          (*printfn)("%s+%"PRIxVADDR" (%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER",%"PRIxREGISTER") ra %"PRIxVADDR" sz %d/n",
                    fn_name(subr), pc - subr, a0, a1, a2, a3, ra, stksize);
 
          if (ra) {
                    if (pc == ra && stksize == 0)/* 出現堆棧長度為零並且當前程序指針為返回地址,則出現循環調用,為異常情況,應直接返回 */
                               (*printfn)("stacktrace: loop!/n");
                    else {
                               pc = ra;
                               sp += stksize;
                               ra = 0;
                               goto loop;
                    }
          } else {/* 返回地址為零表示已經追溯到最頂層 */
finish:
                    if (curlwp)
                               (*printfn)("User-level: pid %d.%d/n", 
                                   curlwp->l_proc->p_pid, curlwp->l_lid);
                    else
                               (*printfn)("User-level: curlwp NULL/n");
          }
}

 

 

下面演示將上面的函數移植到Linux下,應用程序出現異常時的BackTrace顯示

 

應用程序代碼:

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

 

extern int sig_set(int signo);

int func_a(unsigned char* a, unsigned char * b, int c, int d);

int func_b(unsigned char* a);

int func_c(unsigned char* a);

 

int func_a(unsigned char* a, unsigned char * b, int c, int d)

{

    func_b(a);

    return 0;

}

 

int func_b(unsigned char* a)

{

    func_c(a);

    return 0;

}

 

int func_c(unsigned char* a)

{

    *a = "Hello";

    return 0;

}

 

int main(int argc, char* argv[])

{

    unsigned char* a;

    unsigned char buffer[128];

    a = NULL;

        if( sig_set(SIGSEGV) != 0)

                printf("cannot catch SIGSEGV/n");

        if( sig_set(SIGILL) != 0)

                printf("cannot catch SIGILL/n");

 

        printf("trying to catch SIGFPE/n");

        if( sig_set(SIGFPE) != 0)

                printf("cannot catch SIGFPE/n");

 

        printf("trying to catch SIGBUS/n");

        if( sig_set(SIGBUS) != 0)

                printf("cannot catch SIGBUS/n");

 

    func_a(a, buffer, 2, 3);

    return 0;

}

先注冊了4個信號SIGSEGV、SIGILL、SIGFPE、SIGBUS用自己的處理函數來處理。

在函數func_c調用的時候會出現異常,因為a的地址為NULL,這時將整個函數調用的過程顯示出來,輸入如下:

fSegvHandler default sigNo [11]

 pc=00400a84 cause 00000003 badaddr 00000000

 

 d00:00000000 d01:7fff7bd0 d02:00401ee0 d03:00000000

 d04:00000000 d05:7fff7d08 d06:00000002 d07:00000003

 d08:0000d500 d09:0000000a d10:00000000 d11:00000000

 d12:00001000 d13:00000000 d14:0000000a d15:15010000

 d16:00401d40 d17:7fff7df4 d18:00401ca0 d19:00000001

 d20:00400a9c d21:10012608 d22:ffffffff d23:00000000

 d24:00000000 d25:00400a4c d26:00000010 d27:00000000

 d28:10008040 d29:7fff7c88 d30:7fff7c88 d31:00400a30

 k0(d26):00000010

 k1(d27):00000000 gp(d28):10008040

 sp(d29):7fff7c88

 fp(d30):7fff7c88 ra(d31):00400a30

 hi:00000000     lo:00000000

 

Calling backtrace:

Func [400a4c] PC [400a84] Arg0~3 (0,7fff7d08,2,3) RetAddr [400a30] stackSize [16]

Func [4009f4] PC [400a30] Arg0~3 (0,7fff7d08,2,3) RetAddr [4009d8] stackSize [40]

Func [400990] PC [4009d8] Arg0~3 (0,7fff7d08,2,3) RetAddr [400c38] stackSize [40]

Func [400a9c] PC [400c38] Arg0~3 (0,7fff7d08,2,3) RetAddr [2ab18b50] stackSize [176]

Func [2ab189b0] PC [2ab18b50] Arg0~3 (0,7fff7d08,2,3) RetAddr [400790] stackSize [32]

Func [400790] PC [400790] Arg0~3 (0,7fff7d08,2,3) RetAddr [0] stackSize [0]

finished

Segmentation fault

上面輸出的結果可以和實際情況對應起來:

這里是函數調用棧上各個函數的基地址

-bash-3.00$ mips_fp_be-objdump -t test1 | grep 400a4c

00400a4c g     F .text  00000000              func_c

-bash-3.00$ mips_fp_be-objdump -t test1 | grep 4009f4

004009f4 g     F .text  00000000              func_b

-bash-3.00$ mips_fp_be-objdump -t test1 | grep 400990

00400990 g     F .text  00000000              func_a

-bash-3.00$ mips_fp_be-objdump -t test1 | grep 400a9c

00400a9c g     F .text  00000000              main

-bash-3.00$ mips_fp_be-objdump -t test1 | grep 400790

00400790 g     F .text  00000000              __start

PC表示當前函數執行的地址,RetAddr為函數返回地址(和上級函數的PC對應)

例如第一行的輸出:

Func [400a4c] PC [400a84] Arg0~3 (0,7fff7d08,2,3) RetAddr [400a30] stackSize [16]

對應於下面反匯編出來的代碼,看的更清楚。

int func_c(unsigned char* a)

{

  400a4c:   3c1c0fc0    lui gp,0xfc0

  400a50:   279c75f4    addiu   gp,gp,30196

  400a54:   0399e021    addu    gp,gp,t9

  400a58:   27bdfff0    addiu   sp,sp,-16

  400a5c:   afbc0000    sw  gp,0(sp)

  400a60:   afbe000c    sw  s8,12(sp)

  400a64:   afbc0008    sw  gp,8(sp)

  400a68:   03a0f021    move    s8,sp

  400a6c:   afc40010    sw  a0,16(s8)

    *a = "Hello";

  400a70:   8fc30010    lw  v1,16(s8)

  400a74:   8f828018    lw  v0,-32744(gp)

  400a78:   00000000    nop

  400a7c:   24421ee0    addiu   v0,v0,7904

  400a80:   00000000    nop

  400a84:   a0620000    sb  v0,0(v1)

    return 0;

  400a88:   00001021    move    v0,zero

}

  400a8c:   03c0e821    move    sp,s8

  400a90:   8fbe000c    lw  s8,12(sp)

  400a94:   03e00008    jr  ra

  400a98:   27bd0010    addiu   sp,sp,16

 

 

int func_b(unsigned char* a)

{

  4009f4:   3c1c0fc0    lui gp,0xfc0

  4009f8:   279c764c    addiu   gp,gp,30284

  4009fc:   0399e021    addu    gp,gp,t9

  400a00:   27bdffd8    addiu   sp,sp,-40

  400a04:   afbc0010    sw  gp,16(sp)

  400a08:   afbf0020    sw  ra,32(sp)

  400a0c:   afbe001c    sw  s8,28(sp)

  400a10:   afbc0018    sw  gp,24(sp)

  400a14:   03a0f021    move    s8,sp

  400a18:   afc40028    sw  a0,40(s8)

    func_c(a);

  400a1c:   8fc40028    lw  a0,40(s8)

  400a20:   8f998030    lw  t9,-32720(gp)

  400a24:   00000000    nop

  400a28:   0320f809    jalr    t9

  400a2c:   00000000    nop

  400a30:   8fdc0010    lw  gp,16(s8)

    return 0;

  400a34:   00001021    move    v0,zero

}

  400a38:   03c0e821    move    sp,s8

  400a3c:   8fbf0020    lw  ra,32(sp)

  400a40:   8fbe001c    lw  s8,28(sp)

  400a44:   03e00008    jr  ra

  400a48:   27bd0028    addiu   sp,sp,40


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM