调试某些Armv5汇编代码时出现奇怪的内容
我试图通过调试一个简单的ARM组件来学习ARM调试某些Armv5汇编代码时出现奇怪的内容,arm,gdb,Arm,Gdb,我试图通过调试一个简单的ARM组件来学习ARM .global start, stack_top start: ldr sp, =stack_top bl main b . 链接器脚本如下所示: ENTRY(start) SECTIONS { . = 0x10000; .text : {*(.text)} .data : {*(.data)} .bss : {*(.bss)} . = ALIGN(8); . = .
.global start, stack_top
start:
ldr sp, =stack_top
bl main
b .
链接器脚本如下所示:
ENTRY(start)
SECTIONS
{
. = 0x10000;
.text : {*(.text)}
.data : {*(.data)}
.bss : {*(.bss)}
. = ALIGN(8);
. = . +0x1000;
stack_top = .;
}
我在qemu arm模拟器上运行这个。二进制文件在0x10000
处加载。所以我在那里设置了一个断点。一旦bp被击中。我检查了pc
寄存器。它的值是0x10000
。然后在0x10000
处反汇编指令
我看到一条奇怪的评论;0x1000c
这是什么意思?它来自哪里?
Breakpoint 1, 0x00010000 in start ()
(gdb) i r pc
pc 0x10000 0x10000 <start>
(gdb) x /i 0x10000
=> 0x10000 <start>: ldr sp, [pc, #4] ; 0x1000c <start+12> <========= HERE
(gdb) x /i 0x10004
0x10004 <start+4>: bl 0x102b0 <main>
因此0x11520值确实来自链接器脚本符号stack\u top
。但是它与0x10000处的ldr sp[pc,#4]
指令有何关系?
Breakpoint 1, 0x00010000 in start ()
(gdb) i r pc
pc 0x10000 0x10000 <start>
(gdb) x /i 0x10000
=> 0x10000 <start>: ldr sp, [pc, #4] ; 0x1000c <start+12> <========= HERE
(gdb) x /i 0x10004
0x10004 <start+4>: bl 0x102b0 <main>
添加1-2019年12月20日上午9:29
非常感谢@old_timer的详细回答
我正在读这本书。我从这本书中了解了管道的事情。引述如下:
.global start, stack_top, label2 ;<========== HERE I add a new label2
start:
ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
label2:
bl main
b .
因此,如果管道的事情在今天不再相关是什么原因使得pc
在当前执行的指令之前值为2?
我刚刚发现下面的线程解决了这个问题:
基本上,这只是另一种情况,即人们在推进技术的过程中不断为自己制造错误/缺陷/陷阱
回到这个问题:
- 在我的汇编中,使用的是pc相对寻址
- ARM的PC指针比当前执行的指令早2。(处理好这个问题!)
- (我想我现在可以解释。如果我错了,请随时纠正我。)
我尝试了一个稍微不同的组件,多了一个标签。如下图所示:
.global start, stack_top, label2 ;<========== HERE I add a new label2
start:
ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
label2:
bl main
b .
。全局启动,堆栈顶部,标签2 (我想我现在可以解释了。如果我错了,请随时纠正我。)
我尝试了一个稍微不同的组件,多了一个标签。如下图所示:
.global start, stack_top, label2 ;<========== HERE I add a new label2
start:
ldr sp, =stack_top // sp = &stack_top, as soon as we have the stack ready, we can call C function
label2:
bl main
b .
。全局启动,堆栈顶部,标签2 从指令(例如,ldr
或mov
)访问pc
时,ARM(A32)模式中增加8的偏移量,而Thumb(T32)模式中增加4的偏移量。IIRC这是因为函数调用在旧ARM版本中的工作方式。如第A2.3章第页的ARMv7A架构参考手册中记录了这一点。A2-45
注释;0x1000c实际上是由反汇编程序生成的,用于指示由PC+4计算的地址
旁注:ldr,=
不是实际的指令,而是由汇编程序翻译成1-2条指令和可选的文字值,以最有效的方式获得所需的值
如果您对此感兴趣,我在Cortex-M上一步一步地写了一篇关于学习手臂组件的文章。当从指令(例如ldr
或mov
)访问pc
时,ARM(A32)模式下会增加8的偏移量,Thumb(T32)模式下会增加4的偏移量。IIRC这是因为函数调用在旧ARM版本中的工作方式。如第A2.3章第页的ARMv7A架构参考手册中记录了这一点。A2-45
.global start, stack_top
start:
ldr sp, =stack_top
bl main
b .
注释;0x1000c实际上是由反汇编程序生成的,用于指示由PC+4计算的地址
旁注:ldr,=
不是实际的指令,而是由汇编程序翻译成1-2条指令和可选的文字值,以最有效的方式获得所需的值
如果你对此感兴趣,我在Cortex-M上一步一步地写了一篇关于学习手臂装配的文章
.global start, stack_top
start:
ldr sp, =stack_top
bl main
b .
假设arm模式中有三条指令,堆栈_top值的第一个可能存在的池位于.b之后
_start: ( 0x00000000 )
0x00000000 ldr sp,=stack_top
0x00000004 bl main
0x00000008 b .
0x0000000c stack_top
从你们所展示的,这就是汇编程序分配空间的地方
所以在_start+12是堆栈_top值的位置。伪代码ldr sp,=stack_top会转换为mov或pc相对负载。由于历史原因,pc领先两位,这在今天是零相关性的,有些体系结构pc是当前指令,有些是下一条指令可变长度的地址,而在arm(aarch32)和thumb的情况下,pc领先两位,因此8。因此,地址0x00000000处的指令到达0x0000000C的pc相对负载为0xC-8=4。因此,ldr sp,[pc,#4]
现在,该地址的内容是链接器在链接时计算的链接器脚本中所要求的内容。你在那里放了一些代码,然后填充了一些没有显示其余代码的内容,本可以让这成为一个完整的示例,但不管是哪种方式,从你的帖子中链接器最终都会计算0x11520
因此,反向工程您的问题和意见,我们看到二进制开始(一旦链接)
在arm模式下,第一条指令将按照您的要求将值0x11520加载到堆栈指针中。这里没有什么奇怪或错误
0x1000C只是说明地址0x1000C是距离最近的标签_起点12的偏移量。有时这是有用的信息
如果您添加了nop或其他一些代码,则使用伪指令而不定义池,汇编程序将尝试找到一个主池
.global start, stack_top
start:
ldr sp, =stack_top
bl main
nop
b .
然后,汇编器很可能现在会在pc+8上设置,在链接之后,pc+8将是0x10010,如果没有其他更改,堆栈指针可能位于相同的值或更远处的4(或更多),这取决于工具沿途进行的对齐和填充
关键是,如果管道在实际产品中曾经这样做过,那么它就不再以这种方式工作,因此不要将其视为管道,就像mips中的分支阴影指令在今天(启用时)具有任何相关意义一样。对于每个具有pc相对寻址的指令集,您需要知道规则,是该指令的地址(不太常见)、下一条指令的地址(最常见)还是前两条指令的地址,还是其他地址。同样地,人们也会在胸罩里硬编码一段时间