我以为我会先花更多时间讨论这件事。 Valgrind没有报告任何事情,事实上它并没有与Valgrind一起崩溃。
char* a = "HI";
char* b = strdup(a);
(gdb)print b =>
$8 = 0xffffffffe8003680 <Address 0xffffffffe8003680 out of bounds>
这只发生在我动态加载的共享库中(用dlopen加载)。我不知道什么可能导致这种情况。我剥离了该库中的所有内容,其中只有这两行。你能帮我调试吗?
如果我现在尝试访问b
:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6
这里可能出现什么问题?
回溯:
Program received signal SIGSEGV, Segmentation fault. 0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6 (gdb) bt #0 0x00007ffff76b3d0a in strchrnul () from /lib64/libc.so.6 #1 0x00007ffff767088a in vfprintf () from /lib64/libc.so.6 #2 0x00007ffff767af79 in printf () from /lib64/libc.so.6 #3 0x00007ffff722678c in _mkp_stage_30 (plugin=0x61f420, cs=0x7fffe8002040, sr=0x7fffe8003070) at hello.c:68 #4 0x000000000040f93a in mk_plugin_stage_run (hook=16, socket=18, conx=0x0, cs=0x7fffe8002040, sr=0x7fffe8003070) at mk_plugin.c:558 #5 0x000000000040c501 in mk_http_init (cs=0x7fffe8002040, sr=0x7fffe8003070) at mk_http.c:255 #6 0x0000000000404870 in mk_request_process (cs=0x7fffe8002040, sr=0x7fffe8003070) at mk_request.c:510 #7 0x0000000000404d75 in mk_handler_write (socket=18, cs=0x7fffe8002040) at mk_request.c:630 #8 0x000000000040b446 in mk_conn_write (socket=18) at mk_connection.c:130 #9 0x0000000000409352 in mk_epoll_init (efd=12, handler=0x7fffe8001690, max_events=202) at mk_epoll.c:102 #10 0x0000000000409b4e in mk_sched_launch_worker_loop (thread_conf=0x61a5a0) at mk_scheduler.c:196 #11 0x00007ffff79c2f05 in start_thread () from /lib64/libpthread.so.0 #12 0x00007ffff770553d in clone () from /lib64/libc.so.6
答案 0 :(得分:11)
您需要添加
#include <string.h>
获取strdup()
的声明。
或者,如果您已经拥有它,则需要以使strdup()
可见的方式调用编译器(有关详细信息,请参见下文)。 strdup()
由POSIX定义,而不是由ISO C定义。
gcc默认启用相应的宏,但使用-ansi
或-std=c99
将禁用它们。您还可以在源文件的顶部添加适当的#define
。
在没有可见声明的情况下,编译器假定(根据C90规则)strdup()
返回int
。这导致未定义的行为。特别是,如果int
是32位而char*
是64位,那么坏事就会发生。 (根据C99规则,调用没有可见声明的函数是违反约束的。)
您还应该提高编译器的警告级别,注意警告。这样的问题应该在编译时出现;你不应该诊断运行时行为。
更新:
以下内容基于我自己的Ubuntu 11.04系统上的文档和实验。它可能适用于任何使用glibc的系统;其中一些可能适用于非glibc系统。 man strdup
和man feature_test_macros
了解详情。
要使用strdup()
,您必须拥有#include <string.h>
才能使声明可见。 (如果省略这个,编译器不一定会抱怨,但无论如何你都需要它。)
此外,您需要在 #include <string.h>
#define _SVID_SOURCE
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500 /* or greater */
#define _XOPEN_SOURCE
#define _XOPEN_SOURCE_EXTENDED
#define _POSIX_C_SOURCE 200809L /* or greater */
执行此操作的最简单方法是 not 使用gcc的-ansi
或-std=...
选项(或clang的等效选项); gcc默认启用其中一些宏。
如果您想使用-ansi
或std=c99
进行编译,那么您可以使用显式{{1}显式设置上述宏之一(如果您使用第4个备用,则为两个)在源代码中(可能是最佳解决方案),或者使用例如#define
。
和,正如我之前提到的,你应该提高编译器的警告级别。对于gcc,我通常使用:
gcc -std=c99 -D_XOPEN_SOURCE=500
gcc -std=c99 -pedantic -Wall -Wextra -O3
启用优化;副作用是它能够进行必要的分析以执行那些优化,这可以检测在较低优化级别下不明显的许多问题。
所有这些严重性的原因是-O3
未由ANSI / ISO C标准定义(委员会选择不包括它),但它由POSIX定义 - 其声明在{ {1}},ISO C标准标头之一。在严格的ISO C合规模式下,strdup()
中可能无法显示名称<string.h>
。你必须采取这些额外的步骤告诉编译器无论如何都要使它可见。 (默认情况下,gcc不是完全符合标准的C编译器,这就是为什么默认情况下它可以使strdup()
可见。)
由于<string.h>
是一个如此简单的函数,您可以考虑在标准ISO C中编写自己的实现并使用它。例如(请注意,保留以strdup()
开头的名称):
strdup()
(我只对其进行了最低限度的测试。)如果str
是您正在使用的唯一POSIX特定功能,那么滚动自己可能是最佳解决方案。如果您正在使用其他POSIX特定功能,那么无论如何您都必须处理所有这些内容,您也可以使用char *dupstr(const char *s) {
char *const result = malloc(strlen(s) + 1);
if (result != NULL) {
strcpy(result, s);
}
return result;
}
本身。
答案 1 :(得分:1)
看起来你错误地调用了printf()。而不是printf(b),你需要做:
printf("%s\n", b);
编辑:在上面添加\ n,感谢Jonathan。