glibc 竟然也是一个可执行文件

查看系统下 glibc 版本号时,发现了一个有趣的事情,就是 glibc 竟然可以当作可执行文件进行./ 运行。虽然运行起来后,只是打印了一下版本号就退出了。抱着从张银奎老师那里学到的 “格物” 态度,通过 gdb 调试一下,查看究竟。

有趣现象

随意查看一个可执行文件,找到链接的 glibc 库的路径。

1
2
3
4
lhx@ubuntu:~$ ldd a.out 
linux-vdso.so.1 (0x00007ffd65393000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f2c14cd5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2c14eeb000)

然后直接运行 /libc.so.6,没有报错,而是打印了下 glibc 版本信息便自动退出了。

1
2
3
4
5
6
7
8
9
10
11
lhx@ubuntu:~$ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 9.4.0.
libc ABIs: UNIQUE IFUNC ABSOLUTE
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
lhx@ubuntu:~$

gdb 调试探索过程

接下来,打算用 gdb 调试下,分析下这个有趣的现象。

定位入口函数

安装好 glibc 调试符号后,开始调试,发现在 main 函数设断点无法识别,推测入口函数可能不是 main。

1
2
3
4
5
6
lhx@ubuntu:~$ gdb /lib/x86_64-linux-gnu/libc.so.6
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...
Reading symbols from /usr/lib/debug/.build-id/18/78e6b475720c7c51969e69ab2d276fae6d1dee.debug...
(gdb) b main
Function "main" not defined.
(gdb)

然后通过猜测模糊搜索下关于 main 关键字的函数,发现 version.c 文件里__libc_main(void) 函数比较接近,还可以结合源码来定位入口函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(gdb) info function main*
All functions matching regular expression "main*":

File ../csu/libc-start.c:
129: int __libc_start_main(int (*)(int, char **, char **), int, char **, int (*)(int, char **, char **), void (*)(void), void (*)(void), void *);

File bindtextdom.c:
327: char *__bind_textdomain_codeset(const char *, const char *);
318: char *__bindtextdomain(const char *, const char *);

File finddomain.c:
58: struct loaded_l10nfile *_nl_find_domain(const char *, char *, const char *, struct binding *);
189: void _nl_finddomain_subfreeres(void);

File genops.c:
127: void _IO_switch_to_main_get_area(FILE *);

File getdomain.c:
32: int __GI_getdomainname(char *, size_t);

File getdomainname_chk.c:
22: int __getdomainname_chk(char *, size_t, size_t);

File getnameinfo.c:
87: static char *nrl_domainname(void);

File loadmsgcat.c:
752: void _nl_load_domain(struct loaded_l10nfile *, struct binding *);
1288: void _nl_unload_domain(struct loaded_domain *);

File res_hconf.c:
536: void _res_hconf_trim_domain(char *);
561: void _res_hconf_trim_domains(struct hostent *);
103: static const char *arg_trimdomain_list(const char *, int, const char *);

File textdomain.c:
64: char *__textdomain(const char *);

File version.c:
69: void __libc_main(void);

File wgenops.c:
55: void __GI__IO_switch_to_main_wget_area(FILE *);
(gdb)

显示源码

把断点设到__libc_main 后,发现程序停下来了,但是找不到源码信息。

1
2
3
4
5
6
7
8
9
10
(gdb) b __libc_main
Breakpoint 1 at 0x241c0: file version.c, line 70.
(gdb) r
Starting program: /usr/lib/x86_64-linux-gnu/libc.so.6
warning: Probes-based dynamic linker interface failed.
Reverting to original interface.

Breakpoint 1, __libc_main () at version.c:70
70 version.c: 没有那个文件或目录.
(gdb)

然后手动设置下源码搜索路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) directory ~/glibc/glibc-2.31/csu/
Source directories searched: /home/lhx/glibc/glibc-2.31/csu:$cdir:$cwd
(gdb) list
65 Running the library as a program will get here. */
66
67 extern void __libc_main (void) __attribute__ ((noreturn));
68 void
69 __libc_main (void)
70 {
71 __libc_print_version ();
72 _exit (0);
73 }
(gdb)

找到根源

接着单步调试,并进入函数一探究竟。发现版本信息正是被保存在 banner 数组里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
(gdb) s
71 __libc_print_version ();
(gdb) s
__libc_print_version () at version.c:45
45 __write (STDOUT_FILENO, banner, sizeof banner - 1);
(gdb) bt
#0 __libc_print_version () at version.c:45
#1 __libc_main () at version.c:71
(gdb) p banner
$1 = "GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\nCopyright (C) 2020 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.\nThere is NO wa"...
(gdb) set print elements unlimited
(gdb) p banner
$2 = "GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.9) stable release version 2.31.\nCopyright (C) 2020 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions.\nThere is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE.\nCompiled by GNU CC version 9.4.0.\nlibc ABIs: UNIQUE IFUNC ABSOLUTE\nFor bug reporting instructions, please see:\n<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.\n"

然后通过 gdb 直接进去查看下源码,发现 banner 是一个固定写死的字符数组:

GIF 2023-5-21 20-59-13

最后执行完版本打印函数__libc_print_version,glibc 就直接 _exit 退出了。

image-20230521210544482