使用 printf 在 malloc 中创建运行时存根时出现分段错误

Segmentation fault when making a runtime stub in malloc using printf

提问人:zkh 提问时间:2/24/2023 最后编辑:Employed Russianzkh 更新时间:2/25/2023 访问量:106

问:

我正在根据CSAPP一书的原理做一些关于动态库的实验,当我运行链接我的运行时存根库的程序时,我遇到了分段错误。

库如下。

 #include <dlfcn.h>
 #include <stdlib.h>
 #include <stdio.h>

// int data;

void *malloc(size_t size) {
    void *(*mallocp)(size_t size);
    char *error;

    mallocp = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc");
    if ((error = dlerror()) != NULL) {
        fputs(error, stderr);
        exit(1);
    }
    char *ptr = NULL;
    ptr = (char*)mallocp(size);

    // printf("malloc(%d) @ %p\n", (int)size, 0);
    return ptr;
}

run 来编译它。 然后编译 main,它只是调用 malloc。 并运行可执行文件,然后在启动过程中出现分段错误。gcc -fpic -shared dynamic.cpp -ldl -o dynamic.so -ggcc -g test.cppexport LD_PRELOAD

注释掉 printf 后,它工作正常。 有人告诉我这是因为 printf 调用了 malloc。然后会发生什么?他们循环往复?那为什么是segmenattion的错误呢? 代码与书中几乎相同。我想这与我的 printf 的 gcc 的实现有关。 为了在我的库中使用 printf,我应该怎么做?

Linux 分段-故障 堆栈-溢出 ld-preload

评论

0赞 Armali 2/24/2023
他们以一个循环的方式互相称呼,这是真的。当调用堆栈溢出时,会发生分段错误。
0赞 zkh 2/24/2023
@Armali 但是 seg 错误发生在启动期间,在进入 main 之前。我想这与动态链接有关。
0赞 Armali 2/24/2023
进入 main 之前,您如何知道 seg 故障发生在启动期间

答:

1赞 Employed Russian 2/24/2023 #1

注释掉 printf 后,它工作正常。有人告诉我这是因为 printf 调用了 malloc。

是的。

然后会发生什么?他们循环往复?

是的。

那为什么是segmenattion的错误呢?

因为你得到了无限的递归,这会导致(鼓声!堆栈溢出

您的计算机没有无限堆栈。

P.S. 在 GDB 下运行二进制文件会很有启发性。

附注

export LD_PRELOAD和。。。

这是一件相对危险的事情:以这种方式设置将影响(并导致崩溃)当前 shell 中的所有命令。LD_PRELOAD

最好为下一个命令设置变量,如下所示:

$ env LD_PRELOAD=./dynamic.so ./a.out

如果使用和类似的 shell,你也可以做等效的:bash

$ LD_PRELOAD=./dynamic.so ./a.out

更新:

事实是,我使用了 gdb,它说 seg 错误发生在启动期间,而没有提供任何其他信息。

在处理像 这样的低级例程时,你需要有创造力。使用这个答案中的技巧,您可以毫不费力地弄清楚发生了什么。malloc

您还需要从外部连接 GDB -- 如果您在使用 GDB 命令之前进行设置,则会影响您的 shellLD_PRELOADrunLD_PRELOAD

下面是一个示例:

$ cat dynamic.c
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

volatile int done = 0;

void *malloc(size_t size) {
    void *(*mallocp)(size_t size);
    char *error;

    while (!done) { }

    mallocp = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc");
    if ((error = dlerror()) != NULL) {
        fputs(error, stderr);
        exit(1);
    }
    char *ptr = NULL;
    ptr = (char*)mallocp(size);

    printf("malloc(%d) @ %p\n", (int)size, 0);
    return ptr;
}

$ gcc -g -fPIC -o dynamic.so dynamic.c

$ LD_PRELOAD=./dynamic.so ./a.out &
[1] 476197

$ gdb -p 476197

0x00007f52b5f5a156 in malloc (size=1) at dynamic.c:11
11          while (!done) { }
(gdb) bt
#0  0x00007f52b5f5a156 in malloc (size=1) at dynamic.c:11
#1  0x0000561590f6814b in main () at main.c:1
(gdb) set var done = 1
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00007f52b5ea7e2f in __GI__dl_catch_exception (exception=exception@entry=0x7ffc04e7d070, operate=0x7f52b5dde3b0 <dlsym_doit>, args=0x7ffc04e7d110) at ./elf/dl-error-skeleton.c:175
175     ./elf/dl-error-skeleton.c: No such file or directory.
(gdb) bt 20
#0  0x00007f52b5ea7e2f in __GI__dl_catch_exception (exception=exception@entry=0x7ffc04e7d070, operate=0x7f52b5dde3b0 <dlsym_doit>, args=0x7ffc04e7d110) at ./elf/dl-error-skeleton.c:175
#1  0x00007f52b5ea7f4f in __GI__dl_catch_error (objname=0x7ffc04e7d0c8, errstring=0x7ffc04e7d0d0, mallocedp=0x7ffc04e7d0c7, operate=<optimized out>, args=<optimized out>) at ./elf/dl-error-skeleton.c:227
#2  0x00007f52b5ddddc7 in _dlerror_run (operate=operate@entry=0x7f52b5dde3b0 <dlsym_doit>, args=args@entry=0x7ffc04e7d110) at ./dlfcn/dlerror.c:138
#3  0x00007f52b5dde455 in dlsym_implementation (dl_caller=<optimized out>, name=<optimized out>, handle=<optimized out>) at ./dlfcn/dlsym.c:54
#4  ___dlsym (handle=<optimized out>, name=<optimized out>) at ./dlfcn/dlsym.c:68
#5  0x00007f52b5f5a179 in malloc (size=1024) at dynamic.c:13
#6  0x00007f52b5dce76c in __GI__IO_file_doallocate (fp=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/filedoalloc.c:101
#7  0x00007f52b5ddbf50 in __GI__IO_doallocbuf (fp=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/libioP.h:947
#8  __GI__IO_doallocbuf (fp=fp@entry=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/genops.c:342
#9  0x00007f52b5ddb318 in _IO_new_file_overflow (f=0x7f52b5f2c760 <_IO_2_1_stdout_>, ch=-1) at ./libio/fileops.c:744
#10 0x00007f52b5dda4de in _IO_new_file_xsputn (n=7, data=<optimized out>, f=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/libioP.h:947
#11 _IO_new_file_xsputn (f=0x7f52b5f2c760 <_IO_2_1_stdout_>, data=<optimized out>, n=7) at ./libio/fileops.c:1196
#12 0x00007f52b5db53ae in outstring_func (done=0, length=7, string=<error reading variable: Cannot access memory at address 0x4e7d2e0>, s=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ../libio/libioP.h:947
#13 __vfprintf_internal (s=0x7f52b5f2c760 <_IO_2_1_stdout_>, format=<error reading variable: Cannot access memory at address 0x4e7d2e0>, ap=ap@entry=0x7ffc04e7d820, ) at ./stdio-common/vfprintf-internal.c:767
#14 0x00007f52b5dab4fb in __printf (format=<optimized out>) at ./stdio-common/printf.c:33
#15 0x00007f52b5f5a1e8 in malloc (size=1024) at dynamic.c:21
#16 0x00007f52b5dce76c in __GI__IO_file_doallocate (fp=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/filedoalloc.c:101
#17 0x00007f52b5ddbf50 in __GI__IO_doallocbuf (fp=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/libioP.h:947
#18 __GI__IO_doallocbuf (fp=fp@entry=0x7f52b5f2c760 <_IO_2_1_stdout_>) at ./libio/genops.c:342

在这里你可以清楚地看到呼叫呼叫...mallocprintfmalloc

此时的堆栈有多深?

(gdb) bt -4
#42923 __vfprintf_internal (s=0x7f52b5f2c760 <_IO_2_1_stdout_>, format=<error reading variable: Cannot access memory at address 0x567a1c0>, ap=ap@entry=0x7ffc0567a700, ) at ./stdio-common/vfprintf-internal.c:767
#42924 0x00007f52b5dab4fb in __printf (format=<optimized out>) at ./stdio-common/printf.c:33
#42925 0x00007f52b5f5a1e8 in malloc (size=1) at dynamic.c:21
#42926 0x0000561590f6814b in main () at main.c:1

它有 42926 个级别深。

评论

0赞 zkh 2/25/2023
事实是,我使用了 gdb,它说 seg 错误发生在启动期间,而没有提供任何其他信息。它说“启动程序:/home/hzk/a.out 在启动程序期间终止,并带有信号 SIGSEGV,分段故障。我用LD_DEBUG看看发生了什么,发现动态链接器,我想,在一个无限循环中不断绑定malloc。符号=malloc;在 file=/lib/x86_64-linux-gnu/libc.so.6 中查找 [0] 600:将文件 ./dynamic.so [0] 绑定到 /lib/x86_64-linux-gnu/libc.so.6 [0]:正常符号“malloc””
0赞 Employed Russian 2/25/2023
@zkh我已经更新了答案。您可能正在查看错误的过程(您的外壳)。LD_DEBUG