使用Android NDK进行内存损坏调试

3
我正在开发一款安卓应用,但在本地代码部分出现了段错误。具体来说,在一个无返回值函数返回给它的调用者时出现了问题。为了更好地诊断问题,我在被调用函数的结尾处添加了日志输出,并在调用该函数的调用函数中添加了另一个日志输出(抱歉这个词游戏)。在logcat中,第一个日志消息被打印,而第二个则没有(应用程序崩溃)。
考虑到可能存在内存损坏的情况,我决定激活malloc debug(在adb shell中输入“setprop libc.debug.malloc 10”)。然后,在被调用函数结尾的日志消息后,我在logcat中获得了以下信息:
D/MyApp - NativeSide(12778):  I am the callee function and I am about to return!
E/libc    (12778): *** FREE CHECK: buffer 0x82869900 corrupted 16 bytes before allocation
E/libc    (12778): call stack:
E/libc    (12778):  0: 8000e3ea
E/libc    (12778):  1: 8000e49c
E/libc    (12778):  2: 8000e4e2
E/libc    (12778):  3: 8000e540
E/libc    (12778):  4: afd14ccc
E/libc    (12778):  5: 81258188
E/libc    (12778):  6: 81258188
E/libc    (12778):  7: 81258188
E/libc    (12778):  8: 81258188
E/libc    (12778):  9: 81258188
E/libc    (12778): 10: 81258188
E/libc    (12778): 11: 81258188
E/libc    (12778): 12: 81258188
E/libc    (12778): 13: 81258188
E/libc    (12778): 14: 81258188
E/libc    (12778): 15: 81258188
E/libc    (12778): 16: 81258188
E/libc    (12778): 17: 81258188
E/libc    (12778): 18: 81258188
E/libc    (12778): 19: 81258188

我找不到任何有关如何解密此输出的信息。每行显示的数字在每次应用程序启动时都会更改。我希望能够利用这些信息作为故障发生位置的线索,因为我无法从代码中找到它。我还尝试使用“-fstack-check”标志构建本机库,但我无法确定日志中是否有更多信息(似乎没有,但我可能错过了),或者是否需要执行其他操作才能获得更多信息。

此外,在“FREE CHECK:”消息之后,以下是堆栈转储。

I/DEBUG   (12311): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG   (12311): Build fingerprint: 'google/soju/crespo:2.3/GRH55/79397:user/release-keys'
I/DEBUG   (12311): pid: 12778, tid: 12907  >>> com.ntrack.tuner <<<
I/DEBUG   (12311): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad
I/DEBUG   (12311):  r0 deadbaad  r1 45ea374c  r2 00000027  r3 00000000
I/DEBUG   (12311):  r4 00000080  r5 45ea374c  r6 8003422e  r7 45ea37b4
I/DEBUG   (12311):  r8 45da4000  r9 a811eca5  10 00100000  fp 00000001
I/DEBUG   (12311):  ip ffffffff  sp 45ea3738  lr 8000f623  pc 8000f650  cpsr 20000030
I/DEBUG   (12311):  d0  3f9664f48406d639  d1  3f8226e3e96e8495
I/DEBUG   (12311):  d2  3faba1ba1bb34201  d3  0000000000000000
I/DEBUG   (12311):  d4  3d7943379e56fd24  d5  3d8f940585cd5f95
I/DEBUG   (12311):  d6  3f2cf2145b888497  d7  3f2cf214636d85f8
I/DEBUG   (12311):  d8  0000000000000000  d9  0000000000000000
I/DEBUG   (12311):  d10 0000000000000000  d11 0000000000000000
I/DEBUG   (12311):  d12 0000000000000000  d13 0000000000000000
I/DEBUG   (12311):  d14 0000000000000000  d15 0000000000000000
I/DEBUG   (12311):  scr 20000010
I/DEBUG   (12311):

无论是对典型问题的建议,还是如何使用malloc调试信息,都会非常有帮助,谢谢!


请提供具体的文本以便翻译。 - athos
2个回答

11

malloc调试属性可能会在你分配的区域前后设置一些特殊的数字。然后,在释放时,它将检查这些区域以确保魔术数字仍然存在。

例如,如果你分配了1024字节:

char * p = malloc(1024);

malloc调试代码实际上会分配你请求的1024字节以及其周围的一些额外空间:

[ 32 bytes ---- | -------- 1024 bytes ------| ---- 32 bytes ]
^ 0xc0000000    ^ 0xc0000020

该库将在这32个字节中写入一个特殊值:

[ 32 bytes ---- | -------- 1024 bytes ------| ---- 32 bytes ]
[  0xdeadd00d   |                           | 0xdeadd00d    ]
^ 0xc0000000    ^ 0xc0000020

该库将p返回值设为0xc0000020,并在内部保存了0xc0000000、大小等信息。然后你的函数以某种方式使用了已分配的区域:

memset(p, 0, 1025);

注意这行代码复制了超过1024个字节,这将会在最后32个字节的魔术区域写入一个0(请注意最后32个字节中的0,应该是0xdeadd00d):

[ 32 bytes ---- | -------- 1024 bytes ------| ---- 32 bytes ]
[  0xdeadd00d   |  000...             ...00 | 0x0eadd00d    ]
^ 0xc0000000    ^ 0xc0000020  (address)

当你的函数调用free:

free(p);
图书馆将检查前后32个字节是否仍为0xdeadd00d。由于您的函数覆盖了最后32个字节,因此它会打印出像您发布的错误信息一样的错误。
这只是malloc调试检查如何工作的示例。如果您想确切地了解malloc调试检查及其工作原理,请转到Android源代码的bionic目录,并搜索您设置的属性libc.debug.malloc
检查您在被调用函数中对已分配内存的使用方式。您可能正在写入已分配区域之外的区域。

1
谢谢!我的错误在于,我想要为一个包含 100 个 uint32_t 的数组进行内存分配。我调用了 memAlloc(100) 并使用长度为 0->99 的 for 循环写入了数组。这显然会引起错误,因为我超出了已分配的范围! memAlloc(100*sizeof(uin32_t))解决了问题。希望这能对某些人有所帮助! - Rishabh Bhardwaj

3
对我来说,这意味着我的Android应用程序的本地部分发生了段错误,即在一个无返回值函数返回给其调用者的时刻发生了堆栈损坏(比堆损坏更严重)。你从这个函数返回时,存储在堆栈上的状态是什么(以及它调用的每个函数的状态)?
你看到的调用堆栈输出应该是在检测到损坏时堆栈上每个函数的地址。你需要知道库加载的地址,以将它们映射回你的.so文件中的符号。(我认为这个问题会有所帮助:如何在Android中使用addr2line
堆栈转储的顶部重复出现的81258188也表明您可能已经超出了堆栈底部(通过过多递归)。如果您不知道代码中有任何有意的递归,那么找出库加载的位置并将其映射回您的代码可能会有所帮助。

谢谢大家的回答。昨天在一个早期被调用的函数中更改了一些内容后,问题已经解决了,这影响了故障函数的行为方式,我还没有完全理解,但我认为这也与堆栈破坏有关,因为 - 如你所猜测的那样 - 在函数中深处有递归部分,需要进一步调查。我会尽快核实并回来处理此事。 - athos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接