提问人:runningupthatroad 提问时间:9/13/2022 最后编辑:Rachid K.runningupthatroad 更新时间:9/20/2022 访问量:192
定义我自己的 malloc 后分段错误?
Segmentation fault after defining my own malloc?
问:
为什么这会导致分段错误?
#include <stddef.h>
void *malloc(size_t s) {
};
int main() {
printf("lol");
}
我是这样编译的:
gcc -o l lol.c
我的猜测是调用.printf()
malloc()
答:
标准库中的某些内容正在调用,期望它返回一个可用的内存地址,并向该地址写入某些内容。malloc()
在 Unixy(或至少是 Linuxy)平台上,当您在主程序中定义库函数时,它会覆盖任何其他库中的函数,即使库调用它,即使定义它的同一库调用它。
评论
根据 C 语言规范,将您自己的标准库函数定义作为具有外部链接的函数(这是函数的默认值)会产生未定义的行为。这些名称保留用于此类用途(C17 7.1.3)。您已经观察到这种行为的许多可能表现之一。
您至少有四种选择:
- 只需使用标准库的实现即可。
- 使用不同的名称定义函数。例如。然后,您需要使用该名称来调用它,尽管您可以通过使用宏来伪装它。
my_malloc()
- 声明您的函数(赋予其内部链接)。然后,它可以与标准库函数同名,但只有在相同的翻译单元(大致为源文件)中定义的函数才能通过该名称调用它。
static
- 使用特定 C 实现的特定于实现的条款(见下文)。
一些 C 实现为程序提供了特定于实现的规定,以提供至少某些库函数的自己的版本。Glibc 就是其中之一。但是,这些规定受到很大的限制。
首先,您可以预期,实现将要求替换函数提供相同的二进制接口,并正确实现语言指定的行为。(您的函数不执行后者。
其次,如果函数是一组相关函数的一部分,您可能会发现实现需要您替换整个集合。事实上,Glibc 文档说“替换 malloc”涉及为所有这些函数提供替换:、、、。您的程序也不会这样做。Glibc 文档还建议为其他几个函数提供替代品,并建议如果不这样做,虽然本身不会损害任何 Glibc 函数,但可能会破坏一些程序:、、*、、、、。但是,后者与您的特定示例无关。
malloc
malloc
free
calloc
realloc
aligned_alloc
cfree
malloc_usable_size
memalign
posix_memalign
pvalloc
valloc
*只有非常古老的程序才需要。
评论
void *__wrap_malloc(size_t size)
要验证您的函数是否覆盖了 C 库中的同级函数并被 调用,您可以通过调用 system call 在终端(文件描述符编号 1)上记录跟踪:malloc()
printf()
write()
#include <stdio.h>
#include <unistd.h>
void *malloc(size_t s) {
write(1, "I am called\n", sizeof("I am called\n") - 1);
};
int main() {
printf("lol");
}
编译后,程序显示:
$ gcc -g overm.c -o overm
$ ./overm
I am called
Segmentation fault (core dumped)
使用调试器对生成的核心转储文件进行分析,如下所示,显示了崩溃时调用堆栈的状态:gdb
$ gdb overm core
[...]
Reading symbols from overm...
[New LWP 8494]
Core was generated by `./overm'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x00007fd4bf7d6dc8 in _IO_new_file_overflow (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, ch=108) at fileops.c:781
781 fileops.c: No such file or directory.
(gdb) where
#0 0x00007fd4bf7d6dc8 in _IO_new_file_overflow (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, ch=108) at fileops.c:781
#1 0x00007fd4bf7d8024 in __GI__IO_default_xsputn (n=<optimized out>, data=<optimized out>, f=<optimized out>) at libioP.h:948
#2 __GI__IO_default_xsputn (f=f@entry=0x7fd4bf9336a0 <_IO_2_1_stdout_>, data=<optimized out>, n=n@entry=3) at genops.c:370
#3 0x00007fd4bf7d56fa in _IO_new_file_xsputn (n=3, data=<optimized out>, f=<optimized out>) at fileops.c:1265
#4 _IO_new_file_xsputn (f=0x7fd4bf9336a0 <_IO_2_1_stdout_>, data=<optimized out>, n=3) at fileops.c:1197
#5 0x00007fd4bf7bc972 in __vfprintf_internal (s=0x7fd4bf9336a0 <_IO_2_1_stdout_>, format=0x5556655db011 "lol", ap=ap@entry=0x7fff657394a0,
mode_flags=mode_flags@entry=0) at ../libio/libioP.h:948
#6 0x00007fd4bf7a7d3f in __printf (format=<optimized out>) at printf.c:33
#7 0x00005556655da1ab in main () at overm.c:8
上面的显示,在源代码的第 33 行调用时发生了崩溃。让我们在函数上设置一个断点,然后从调试器重新运行程序:printf()
printf()
(gdb) br printf
Breakpoint 3 at 0x7ffff7e1ac90: file printf.c, line 28.
(gdb) run
Breakpoint 3, __printf (format=0x555555556011 "lol") at printf.c:28
28 printf.c: No such file or directory.
(gdb)
一旦我们在调用时停止,在函数上添加另一个断点(我们以前没有这样做,因为在程序启动期间被内部代码调用了几次)并继续执行:printf()
malloc()
malloc()
(gdb) br malloc
(gdb) continue
Continuing.
Breakpoint 4, malloc (s=140737354002065) at overm.c:4
4 void *malloc(size_t s) {
(gdb) where
#0 malloc (s=140737354002065) at overm.c:4
#1 0x00007ffff7e3ad04 in __GI__IO_file_doallocate (fp=0x7ffff7fa66a0 <_IO_2_1_stdout_>) at filedoalloc.c:101
#2 0x00007ffff7e4aed0 in __GI__IO_doallocbuf (fp=fp@entry=0x7ffff7fa66a0 <_IO_2_1_stdout_>) at libioP.h:948
#3 0x00007ffff7e49f30 in _IO_new_file_overflow (f=0x7ffff7fa66a0 <_IO_2_1_stdout_>, ch=-1) at fileops.c:745
#4 0x00007ffff7e486b5 in _IO_new_file_xsputn (n=3, data=<optimized out>, f=<optimized out>) at libioP.h:948
#5 _IO_new_file_xsputn (f=0x7ffff7fa66a0 <_IO_2_1_stdout_>, data=<optimized out>, n=3) at fileops.c:1197
#6 0x00007ffff7e2f972 in __vfprintf_internal (s=0x7ffff7fa66a0 <_IO_2_1_stdout_>, format=0x555555556011 "lol", ap=ap@entry=0x7fffffffdd90,
mode_flags=mode_flags@entry=0) at ../libio/libioP.h:948
#7 0x00007ffff7e1ad3f in __printf (format=<optimized out>) at printf.c:33
#8 0x00005555555551ab in main () at overm.c:8
(gdb)
当我们在调用时停止时显示的堆栈显示它是源代码中第 33 行调用的结果。malloc()
printf()
评论
void*
malloc
malloc