GDB调试-新手笔记2

用GDB查看任意地址内容

在这里插入图片描述

  • n, the repeat count
    The repeat count is a decimal integer; the default is 1. It specifies how much memory (counting by units u) to display.
  • f, the display format
    The display format is one of the formats used by print, s' (null-terminated string), ori’ (machine instruction). The default is `x’ (hexadecimal) initially. The default changes each time you use either x or print.
  • u, the unit size
    The unit size is any of
    – b Bytes.
    – h Halfwords (two bytes).
    – w Words (four bytes). This is the
    initial default.
    – g Giant words (eight bytes).

举例:
n/f/u三个参数可以一起使用。例如:

命令:x/3uh 0x7ff320

表示,从内存地址0x7ff320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十进制显示无符号整型。

GDB打印数据显示格式:
x (hexadecimal)按十六进制格式显示变量。
d (signed decimal)按十进制格式显示变量。
u (unsigned decimal)按十进制格式显示无符号整型。
o (octal)按八进制格式显示变量。
t (binary)按二进制格式显示变量。
a (address)按十六进制格式显示地址,并显示距离前继符号的偏移量(offset)。常用于定位未知地址(变量)。
c (character)按字符格式显示变量。
f (floating)按浮点数格式显示变量。

ARM寄存器

在这里插入图片描述
在这里插入图片描述

ARM汇编指令集

ARM指令基本格式:

<opcode> {<cond>} {S} <Rd> , <Rn> { , <shift_op2> }
  • <>:内项目必选的
  • {}:内项目可选的
  • cond:条件码,描述指令执行条件
  • S:可选后缀,加上“S”,在指令执行完毕后自动更新CPSR中的条件码标志位的值
  • Rd:ARM指令中的目标操作数总是一个寄存器,通常用Rd表示
  • Rn:存放第1操作数寄存器
  • opcode2:第2操作数,不仅可以是寄存器,还能是立即数,而且能够使用经过位移运算的寄存器和立即数。
    在这里插入图片描述

MOV指令:
指令格式:

MOV {条件}{S}  目的寄存器,源操作数

MOV指令可完成从另一个寄存器、被移位的寄存器或立即数赋值到目的寄存器。其中S选项为指令的操作结果是否操作CPSR中的条件标志位,当没有S选项时指令不更新CPSR中的条件标志位结果。
举例:

MOV R0,R1 ; R0 = R1;
MOV PC,R14 ; PC = R14;
MOV R0,R1,LSL#3 ; R0=R1<<3;

注意:
需要注意的是,这里的立即数是有要求的,不是说任何一个立即数都可以。
要求是:立即数可以由一个8位的常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。

MOV AL,20H;将8位数据20H传送到AL寄存器
MOV AL,[2000H];将2000H单元的内容传送到AL寄存器 如果加了[
]中括号,就表示括号里面的是地址,传送这个地址里的数据。
如果没有中括号,就直接传送这个数据给目标寄存器

对于不合法的立即数,比如要把0xFFF传送到R1中,虽然不能用MOV指令,但是可以用LDR伪指令,用法是
LDR R1,=0xFFF

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
load/store指令:用于寄存器和存储器之间数据传送的指令
在这里插入图片描述
Load指令用于从内存中读取数据放入寄存器中,

LDR{条件} 目的寄存器,<存储器地址>

LDR指令用于从存储器中将一个32位的数据传送到目的寄存器中。当程序计数器PC作为目的寄存器时,指令从存储器中读取的数据被目的地址,从而实现程序流程的跳转。
LDR R0,#8
LDR R0,[R1,R2]
LDR R0,[R1,# 8]
在这里插入图片描述
Store指令用于将寄存器中的数据保存到内存。

STR{条件} 源寄存器,<存储器地址>

STR指令用于从源寄存器中将一个32位数据传送到存储器中。
STR #8,R0
STR R0,[R1,#8]

条件码转表

在这里插入图片描述
跳转指令
  跳转指令用于实现程序流程的跳转,在ARM程序中有两种方法可以实现程序的跳转,一种是使用跳转指令直接跳转,另一种是直接向PC寄存器赋值实现跳转。

  • B{条件} 目标地址   B指令最简单的跳转指令,一旦遇到一个B指令,ARM处理器立即跳转至给定的目标地址,由此处继续执行。跳转指令B限制在当前指令的土32MB范围内
  • BL{条件} 目标地址   BL是一另个跳转指令,在跳转前会将下一条指令的地址复制到R14中,然后跳转到指定的地址运行程序。可以通过将R14的内容重新加载到PC中,并返回到跳转指令之后的那个指令处执行。
    BL是实现子程序调用的一个基本但常用的手段。 (也就**是函数,**最常用的!)
  • BLX{条件} 目标地址   BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态由ARM状态切换到Thumb状态,该指令同时将PC的当前内容到寄存器R14中。当程序使用Thumb指令集,而调用者使用ARM指令集时,可以通过BLX指令实现程序的调用和处理器工作状态的切换。子程序的返回可以通过将寄存器R14复制到PC中来完成。
  • BX{条件} 目标地址   BX指令是带状态切换的跳转指令,跳转到指定的目标地址执行程序。若目标地址寄存器的[0]位为1,则跳转时自动将CPSR中的标志T置位,即把目标地址的代码解释为Thumb指令。若目标地址寄存器的[0]位为0,则跳转时自动将CPSR中的标志T复位,即把目标地址的代码解释为ARM指令。

举例

int main()
{
    unsigned int *pGPFCON = (unsigned int *)0x56000050;
    unsigned int *pGPFDAT = (unsigned int *)0x56000054;


    *pGPFCON = 0x100;

    *pGPFDAT = 0;

    return 0;
}
e3a03456     mov    r3, #1442840576    ; 0x56000000  //把0x56000000放到r3中
e2833050     add    r3, r3, #80 (立即数#80是十进制,转为十六进制是0x50; 0x50            //把r3的值加80  变成0x56000050
e50b3010     str    r3, [fp, #-16]                //把0x56000050放到[4076]       第一局部变量
  
e3a03456     mov    r3, #1442840576    ; 0x56000000  //把0x56000000放到r3中
e2833054     add    r3, r3, #84    ; 0x54            //把r3的值加84 变成0x56000054
0x56000050放到[4072]       第二局部变量
   
e51b2010     ldr    r2, [fp, #-16]                //把内存中[4076]的值0x56000050放到r2中去
e3a03c01     mov    r3, #256    ; 0x100            
e5823000     str    r3, [r2]                      //把0x100写入[0x56000050]内存中去
  
e51b2014     ldr    r2, [fp, #-20]                //把内存中[4072]的值0x56000054放到r2中去
 e3a03000     mov    r3, #0    ; 0x0                
e5823000     str    r3, [r2]                      //把0x0写入[0x56000054]内存中去

nm命令

nm命令主要是用来列出某些文件中的符号(就是一些函数和全局变量等),需要依赖符号表存在,如果符号表删除了就查不到。
执行命令:

nm stack0

在这里插入图片描述
查到main地址: 00010434
这个地址与gdb 调试地址是一致的:

gdb stack0
--> disassemble main

在这里插入图片描述

objdump 反汇编

可以看到所有区段的反汇编代码和地址,这样我们对照着这个输出信息。

objdump -d stack0 > dump.txt

在这里插入图片描述

objdump -D -S stack0 >dump

-D, --disassemble-all Display assembler contents of all sections
-S, --source Intermix source code with disassembly

ARM-栈

栈:栈是一种具有后进先出的数据组织方式,也就是说后存放的先取出,先存放的后取出。栈底是第一个进栈的数据所处位置,栈顶是最后一个数据进栈所处的位置。

满/空栈

根据SP指针指向的位置,栈可以分为满栈和空栈。

满栈:当堆栈指针总是指向最后压入堆栈的数据

空栈:当堆栈指针总是指向下一个将要放入数据的空位置
在这里插入图片描述
ARM采用满栈

升/降栈

根据SP指针移动的方向,栈可以分为升栈和降栈

升栈:随着数据的入栈,SP指针从低地址->高地址移动

降栈:随着数据的入栈,SP指针从高地址->低地址移动
在这里插入图片描述
ARM采用降栈

注:ARM是满降栈

举例:

#include <stdio.h>

int main()
{
    ...
    func1();
    ...
}

int func1()
{
    ...
}

例子中有两个函数(包括主函数main),程序运行起来会有一个栈,但这个栈中有两个栈帧。
在这里插入图片描述
注意是从高地址到低地址的! (SP由高到低,做sub运算)
fp(r11)栈帧指针,栈帧上边界由fp指针界定,下边界有sp指针界定。从main函数进入到func1函数,main函数的上边界和下边界保存在被它调用的栈帧里面。

  • 栈帧(stack frame):就是一个函数所使用的那部分栈,所有函数的栈帧串起来就组成了一个完整的栈。栈帧的两个边界分别由fp(r11)和sp(r13)来限定。

栈帧

在这里插入图片描述
(淡蓝色和绿色分别代表两个栈帧,淡蓝色表示已临时压栈保存了关键信息,可以执行被调函数func了,所以FP和SP指向正在执行的当前栈(绿色的),绿色执行完毕后,会返回淡蓝色,从已压栈的PC、LR、SP和FP取值进行操作)
上图描述的是ARM的栈帧布局方式:

  • main stack frame为调用函数的栈帧,func1 stack
    frame为当前函数(被调用者)的栈帧,栈底在高地址,栈向下增长。

  • 图中FP就是栈基址,它指向函数的栈帧起始地址; SP则是函数的栈指针,它指向栈顶的位置。

  • ARM压栈的顺序依次为当前函数指针PC、返回指针LR、栈指针SP、栈基址FP、传入参数个数及指针、本地变量和临时变量。【这个顺序很重要,自己栈帧的重要信息都保存下来

  • 如果函数准备调用另一个函数,跳转之前临时变量区先要保存另一个函数的参数。

栈的作用

保存局部变量
#include <stdio.h>

int main()
{
    int a;
    a++;
    return a;
}

反汇编:

objdump -D -S stack1 >dump
/*反汇编代码*/
 000083a0 <main>:
 #include <stdio.h>
 
 int main()
 {
     83a0:       e1a0c00d        mov     ip, sp                    
     83a4:       e92dd800        stmdb   sp!, {fp, ip, lr, pc}
     83a8:       e24cb004        sub     fp, ip, #4      ; 0x4
     83ac:       e24dd004        sub     sp, sp, #4      ; 0x4
         int a;
         a++;
     83b0:       e51b3010        ldr     r3, [fp, #-16]
     83b4:       e2833001        add     r3, r3, #1      ; 0x1
     83b8:       e50b3010        str     r3, [fp, #-16]
         return  a;
     83bc:       e51b3010        ldr     r3, [fp, #-16]
 }
/*分析*/
    mov ip,sp        //保存sp到ip
    stmdb sp!,{fp,ip,lr,pc}   /*先对sp-4,再对fp,ip,lr,pc压栈*/
                             //sp=sp-4;push {pc};sp=pc;  /*先压pc*/
                             //sp=sp-4;push {lr};sp=lr; /*压lr*/
                             //sp=sp-4;push {ip};sp=ip;  /*压ip*/
                             //sp=sp-4;push {fp};sp=fp; /*压fp*/
    sub fp,ip,#4        //fp指向ip-4
    sub sp,sp,#4       //开辟一块空间
  
    ldr r3,[fp,#-16]   //临时存放在[fp-16]
    add r3,r3,#1     
    str r3,[fp,#-16]

GDB栈显示

(gdb) bt:显示所有栈帧,backtrace。
(gdb) bt 10:显示前面10个栈帧。
(gdb) bt -10:显示后面10个栈帧。
(gdb) bt full:显示栈帧以及局部变量。
(gdb) bt full 10:显示前面10个栈帧以及局部变量。
(gdb) bt full -10:显示后面10个栈帧以及局部变量。
(gdb) frame <栈帧编号>:进入指定的栈帧中,然后可以查看当前栈帧中的局部变量,以及栈帧内容等信息。
(gdb) info frame <栈帧编号>:可以查看指定栈帧的详细信息。
(gdb) up:进入上层栈帧。
(gdb) down:进入下层栈帧。

参考

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值