FC3000
解決eduke32詭異的crash問題
Crash Log
Thread 1 "eduke32" received signal SIGSEGV, Segmentation fault. 0xb6e25e68 in memset () from /lib//libc.so.0 (gdb) bt #0 0xb6e25e68 in memset () from /lib//libc.so.0 #1 0x001e1e68 in decodeframe ( srcP=0xb4df2bc6 "((\v\v\026!\342Ƴ\001\263\263\263\001\263\001\361\241_,,\017`\032\032!\026(", dstP=0x1d229bb <error: Cannot access memory at address 0x1d229bb>) at source/jmact/animlib.c:180 #2 0x001e212c in renderframe (framenumber=0, pagepointer=0xb4df0b78) at source/jmact/animlib.c:239 #3 0x001e2198 in drawframe (framenumber=0) at source/jmact/animlib.c:253 #4 0x001e2a98 in ANIM_DrawFrame (framenumber=1) at source/jmact/animlib.c:330 #5 0x000cf2d0 in G_PlayAnim (fn=0x26a658 "logo.anm", t=5 '\005') at source/anim.c:261 #6 0x0006bccc in G_DisplayLogo () at source/game.c:8668 #7 0x0007055c in app_main (argc=1, argv=0xbeedbdf4) at source/game.c:9960 #8 0x00250f20 in main (argc=1, argv=0xbeedbdf4) at src/sdlayer.c:208
Crash位置在第11行
static void decodeframe(uint8_t * srcP, uint8_t * dstP) { do { int32_t count=*srcP++; if (!count) /* Short RLE */ { int32_t color = *(srcP+1); count = *(uint8_t *)((srcP += sizeof(int16_t)) - sizeof(int16_t)); Bmemset((dstP += count) - count, color, count); continue; } else if ((count & 0x80) == 0) /* Short copy */ { Bmemcpy((dstP += count) - count, (srcP += count) - count, count); continue; } else if ((count &= ~0x80) > 0) /* short skip */ { dstP += count; continue; } /* long op */ count = B_LITTLE16(*((uint16_t *)((srcP += sizeof(int16_t)) - sizeof(int16_t)))); if (!count) /* stop sign */ return; else if ((count & 0x8000) == 0) /* long skip */ { dstP += count; continue; } else if ((count &= ~0x8000) & 0x4000) /* long RLE */ { int32_t color = *srcP++; count &= ~0x4000; Bmemset((dstP += count) - count, color, count); continue; } /* long copy */ Bmemcpy((dstP += count) - count, (srcP += count) - count, count); } while (1); }
但是仔細追查後,發現有問題是這行
count = B_LITTLE16(*((uint16_t *)((srcP += sizeof(int16_t)) - sizeof(int16_t))));
改寫比較容易看懂的程式
count = B_LITTLE16(*(uint16_t *)srcP); srcP += sizeof(int16_t);
GDB看了一下
count = B_LITTLE16(*((uint16_t *)((srcP += sizeof(int16_t)) - sizeof(int16_t)))); (gdb) p/x count $2 = 0x9d80 (gdb) x/16x srcP-4 0xb4dc9b81: 0x8000 0xd99d 0x0000 0x2605 0x2d80 0x00c1 0x2605 0x2826 0xb4dc9b91: 0x280b 0x0600 0x0b0b 0x0b16 0x1a16 0x1a0b 0x0b1a 0x0b1a
srcP的位址是0xb4dc9b83,所以*(uint_16_t*)srcP應該是拿到0xd99d,但是結果竟然是0x9d80
司徒也不知道為何LDRH是取低位址的值(0xb4dc9b83 ~ 0xb4dc9b82),而不是高位址的值(0xb4dc9b84 ~ 0xb4dc9b83)
(gdb) stepi 0x001e1f08 <+304>: sub r3, r3, #2 => 0x001e1f0c <+308>: ldrh r3, [r3] 0x001e1f10 <+312>: str r3, [r11, #-8] 0x001e1f14 <+316>: ldr r3, [r11, #-8] (gdb) info r r3 0xb4dc9b83 3034356611 (gdb) stepi 0x001e1f08 <+304>: sub r3, r3, #2 0x001e1f0c <+308>: ldrh r3, [r3] => 0x001e1f10 <+312>: str r3, [r11, #-8] 0x001e1f14 <+316>: ldr r3, [r11, #-8] (gdb) info r r3 0x9d80 40320
因此,使用一個workaround的方式修掉
//count = B_LITTLE16(*((uint16_t *)((srcP += sizeof(int16_t)) - sizeof(int16_t)))); count = B_LITTLE16(srcP[0] + (((uint16_t)srcP[1]) << 8)); srcP += sizeof(int16_t);