英文:
Segmentation fault when making a runtime stub in malloc using printf
问题
我正在按照CSAPP书籍的指导进行有关动态库的一些实验,当我运行链接我的运行时存根库的程序时,出现分段错误。
以下是该库的内容:
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
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);
return ptr;
}
使用以下命令进行编译:
gcc -fpic -shared dynamic.cpp -ldl -o dynamic.so -g
然后编译主程序,它只是调用malloc:
gcc -g test.cpp
设置LD_PRELOAD
环境变量并运行可执行文件,然后在启动过程中出现分段错误。
在将printf注释掉后,程序可以正常工作。有人告诉我这是因为printf调用了malloc。然后会发生什么?它们互相调用吗?那为什么会出现分段错误?这段代码几乎与书中的代码相同。我猜测这与我的gcc中printf的实现有关。为了在我的库中使用printf,我应该怎么做?
英文:
I'm doing some experiments about dynamic library by the insturctions of the book CSAPP, and I get segmentation fault when I run the program linking my runtime stub library.
The library is as follows.
#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
gcc -fpic -shared dynamic.cpp -ldl -o dynamic.so -g
to compile it.
Then
gcc -g test.cpp
compile the main which simply calls malloc.
export LD_PRELOAD
and run the executable then get segmentation fault during startup.
After commenting out the printf, it works fine.
Someone tells me it's because printf calls malloc. Then what happens? They call each other in a cycle? Then why the segmenattion fault?
The code is almost the same with the book. I guess it has something to do with the implementation of my gcc of printf.
What should I do in order to use printf in my library?
答案1
得分: 1
> After commenting out the printf, it works fine. Someone tells me it's because printf calls malloc.
取消printf注释后,它正常工作。有人告诉我这是因为printf调用了malloc。
> Then what happens? They call each other in a cycle?
然后会发生什么?它们互相循环调用吗?
> Then why the segmenattion fault?
那为什么会出现段错误?
Because you get infinite recursion, and that causes (drum roll!) stack overflow.
因为你得到了无限递归,这导致了(鼓声!)栈溢出。
Your computer does not have unlimited stack.
你的计算机栈不是无限的。
P.S. Running your binary under GDB shoul be illuminating.
P.P.S.
> export LD_PRELOAD
and ...
这是一个相对危险的做法:以这种方式设置LD_PRELOAD会影响(并导致崩溃)当前shell中的所有命令。
更好的做法是仅为下一个命令设置该变量,像这样:
$ env LD_PRELOAD=./dynamic.so ./a.out
如果使用bash
和类似的shell,也可以进行等效操作:
$ LD_PRELOAD=./dynamic.so ./a.out
更新:
> The fact is that I used gdb and it said the seg fault happened during startup without giving any other information.
事实是我使用了gdb,并且它说段错误发生在启动过程中,没有提供其他信息。
When dealing with low-level routines like malloc
, you need to be creative. Using tricks from [this answer][1], you can figure out what's going on without too much trouble.
处理像malloc
这样的低级例程时,你需要有创造力。使用[这个答案][1]中的技巧,你可以找出发生了什么事情,而不会有太多麻烦。
You also need to attach GDB from the outside -- if you set LD_PRELOAD
before using GDB run
command, the LD_PRELOAD
would affect your shell.
你还需要从外部附加GDB - 如果在使用GDB的run
命令之前设置了LD_PRELOAD
,那么LD_PRELOAD
将影响你的_shell_。
Here is an example:
这里有一个例子:
$ 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/lib
<details>
<summary>英文:</summary>
> After commenting out the printf, it works fine. Someone tells me it's because printf calls malloc.
Yes.
> Then what happens? They call each other in a cycle?
Yes.
> Then why the segmenattion fault?
Because you get infinite recursion, and that causes (drum roll!) _stack overflow_.
Your computer does not have unlimited stack.
P.S. Running your binary under GDB shoul be illuminating.
P.P.S.
> `export LD_PRELOAD` and ...
This is a relatively dangerous thing to do: setting `LD_PRELOAD` this way will affect (and cause to crash) all commands in the current shell.
It is better to set the variable _only_ for the next command, like so:
$ env LD_PRELOAD=./dynamic.so ./a.out
If using `bash` and similar shells, you can also do equivalent:
$ LD_PRELOAD=./dynamic.so ./a.out
**Update:**
> The fact is that I used gdb and it said the seg fault happened during startup without giving any other information.
When dealing with low-level routines like `malloc`, you need to be creative. Using tricks from [this answer][1], you can figure out what's going on without too much trouble.
You also need to attach GDB from the outside -- if you set `LD_PRELOAD` before using GDB `run` command, the `LD_PRELOAD` would affect your _shell_.
Here is an example:
$ 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
Here you can clearly see `malloc` calling `printf` calling `malloc` ...
How deep is the stack at that point?
(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
It's 42926 levels deep.
[1]: https://stackoverflow.com/a/41053352/50617
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论