这是我在Linux上运行应用程序时在一些非常罕见的SEGV上收到的GDB输出,Cortex-A8:
Program received signal SIGSEGV, Segmentation fault.
0xb6341668 in strcpy () at ../ports/sysdeps/arm/armv6/strcpy.S:48
(gdb) info registers
r0 0x161598 1447320
r1 0x153eec 1392364
r2 0x161598 1447320
r3 0x2e 46
r4 0x0 0
r5 0xbb8 3000
r6 0xd8 216
r7 0xbefff408 3204445192
r8 0x0 0
r9 0x0 0
r10 0xb6fff000 3070226432
r11 0xa 10
r12 0x14d1e4 1364452
sp 0xbefff408 0xbefff408
lr 0x80461 525409
pc 0xb6341668 0xb6341668 <strcpy+8>
cpsr 0xf0030 983088
(gdb) disas
Dump of assembler code for function strcpy:
0xb6341660 <+0>: mov r12, r0
0xb6341662 <+2>: pld [r0]
0xb6341666 <+6>: pld [r1]
0xb634166a <+10>: and.w r3, r1, #7
0xb634166e <+14>: rsb r3, r3, #16
0xb6341672 <+18>: ldrb.w r2, [r1], #1
传递给strcpy(上部回溯帧)的堆栈跟踪和值似乎正确,但PC值为0xb6341668,这不是gdb反汇编中任何指令的开头。这是合法的吗?
答案 0 :(得分:4)
正如其他人所指出的那样,PC可以自由指向任何足够对齐的地方 - 这是ARM状态下的4字节边界,或Thumb状态下的2字节边界。
当您查看机器代码时,这种特殊情况会变得更有趣,并考虑Thumb的可变长度编码的重要性:
0: 4684 mov ip, r0
2: f890 f000 pld [r0]
6: f891 f000 pld [r1]
a: f001 0307 and.w r3, r1, #7
e: f1c3 0310 rsb r3, r3, #16
12: f811 2b01 ldrb.w r2, [r1], #1
但是,嘿,我们已经在bugsville,所以谁说我们必须从<strcpy>
开始?让我们尝试拆解同样的东西,但是从<strcpy+4>
开始敲掉前两个半字,并使32位编码不同步:
// 4684 f890 (skipped)
0: f000 f891 bl 0x126
4: f000 f001 bl 0x40000a
8: 0307 lsls r7, r0, #12
a: f1c3 0310 rsb r3, r3, #16
e: f811 2b01 ldrb.w r2, [r1], #1
所以你去,如果你把你的PC指向0xb6341668,它会看到一个完全有效的bl . + 0x400006
,所以如果0xb674166e确实未映射(或映射为不执行)那么你应该从尝试获得一个SEGV执行它。现在,你如何做到这一点完全是另一回事......
答案 1 :(得分:1)
处理器位于Thumb mode,使用16位指令; decode the cpsr
了解它的模式。
答案 2 :(得分:1)
虽然这肯定是问题的根源,但是没有检查处理器以验证它是否在有效的指令边界。实际上没有办法验证它:处理器只是获取指令,如果它看起来像32位或16位,则解码并执行它。
在这种情况下,很可能一条(垃圾)指令实际上未定义,导致对齐或MMU错误,因为它实际上正在运行随机指令。