问题描述
以下程序:
#include< stdio.h>
int main(int argc,char * argv [])
{
for(int j = 0; j< argc; j ++)
printf( %d:%s\n,j,argv [j]);
返回0;
}
内置于静态链接的PIE:
gcc -g -fpie main.c -static-pie -o ld.so
工作正常:
$ ./ld.so foo bar
0:./ld.so
1:foo
2:bar
但是当我将该程序用作另一个程序的ELF解释器时:
$ gcc -g main .c -Wl,-I。/ ld.so -o a.out
它像这样崩溃:
gdb -q ./a.out
(gdb)运行
启动程序:/ tmp /a.out
程序收到信号SIGSEGV,分段错误。
0x00007ffff7da84e2在__ctype_init()中位于ctype-info.c:31
31 * bp =(const uint16_t *)_NL_CURRENT(LC_CTYPE,_NL_CTYPE_CLASS)+ 128;
(gdb)bt
#0 0x00007ffff7da84e2在__ctype_init()在ctype-info.c:31
#1 0x00007ffff7d9e3bf在__libc_init_first(argc = argc @ entry = 1,argv = argv @ entry = 0x7fffffffd728,envp = 0x7fffffffd738)位于../csu/init-first.c:84
#2 0x00007ffff7d575cd位于__libc_start_main(main = 0x7ffff7d56e29 ,argc = 1,argv = 0x7ffff ; __ libc_csu_init> ;, fini = 0x7ffff7d57d70< __ libc_csu_fini> ;, rtld_fini = 0x0,
stack_end = 0x7ffffffdd718)在../csu/libc-start.c:244
#3 0_00007aff7d ./sysdeps/x86_64/start.S:120
为什么?
以上所有地址都在 ./ ld.so
本身之内,因此在其自身的初始化期间崩溃。实际上,自从 ld.so
退出以来,控件就永远不会达到 a.out
。
调试时间比我预期的时间长。
崩溃发生在:
函数__ctype_init的汇编代码转储:
0x00007ffff7da84d0< + 0> ;: mov $ 0xffffffffffffffffa0,%rax
0x00007ffff7da84d7< + 7> ;: mov $ 0xfffffffffffffffff0,%rcx
0x00007ffff7da84de< + 14> ;: mov%fs:(%rax),%rax
=> 0x00007ffff7da84e2< + 18> ;: mov(%rax),%rax
0x00007ffff7da84e5< + 21> ;: mov 0x40(%rax),%rsi
,其中 $ rax == 0
。当 ld.so
本身经历此代码时, $ rax
显然不是NULL。显然在 TLS
设置过程中出了问题,但是什么呢?
事实证明,GLIBC初始化了它的<$ c $来自辅助向量中的 AT_PHDR
中的c> _dl_phdr ,然后遍历所有 Phdr
查找类型为 PT_TLS
的一个。
如果没有一个,则GLIBC假定没有<$必须设置c $ c> TLS 。
运行 ld.so
时内核提供的aux向量直接指向 ld.so
, PT_TLS的
存在,并且一切正常。 Phdr
s
但是当 ld.so
运行间接作为 a.out
的解释器,辅助向量指向 Phdr
s a.out
(而不是 ld.so
-这是按设计的)。由于 a.out
没有任何线程局部变量,因此也没有 PT_TLS
段。 / p>
结论:目前无法使用 -static-pie构建
和GLIBC,除非非常小心地避免线程本地存储。并且避免当前也不选择线程本地存储:琐碎的 ELF
解释器 int main(){return 0; }
仍然有 TLS
个细分,尽管根本没有使用GLIBC的 anything 。
The following program:
#include <stdio.h>
int main(int argc, char *argv[])
{
for (int j = 0; j < argc; j++)
printf("%d: %s\n", j, argv[j]);
return 0;
}
built into a statically linked PIE:
gcc -g -fpie main.c -static-pie -o ld.so
works fine:
$ ./ld.so foo bar
0: ./ld.so
1: foo
2: bar
But when I use that program as an ELF interpreter for another program:
$ gcc -g main.c -Wl,-I./ld.so -o a.out
it crashes like so:
gdb -q ./a.out
(gdb) run
Starting program: /tmp/a.out
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7da84e2 in __ctype_init () at ctype-info.c:31
31 *bp = (const uint16_t *) _NL_CURRENT (LC_CTYPE, _NL_CTYPE_CLASS) + 128;
(gdb) bt
#0 0x00007ffff7da84e2 in __ctype_init () at ctype-info.c:31
#1 0x00007ffff7d9e3bf in __libc_init_first (argc=argc@entry=1, argv=argv@entry=0x7fffffffd728, envp=0x7fffffffd738) at ../csu/init-first.c:84
#2 0x00007ffff7d575cd in __libc_start_main (main=0x7ffff7d56e29 <main>, argc=1, argv=0x7fffffffd728, init=0x7ffff7d57ce0 <__libc_csu_init>, fini=0x7ffff7d57d70 <__libc_csu_fini>, rtld_fini=0x0,
stack_end=0x7fffffffd718) at ../csu/libc-start.c:244
#3 0x00007ffff7d56d6a in _start () at ../sysdeps/x86_64/start.S:120
Why is that?
All the addresses above are within ./ld.so
itself, so it crashes during its own initialization. Indeed the control would never reach a.out
since ld.so
exits.
This took a bit longer to debug than I expected.
The crash is in:
Dump of assembler code for function __ctype_init:
0x00007ffff7da84d0 <+0>: mov $0xffffffffffffffa0,%rax
0x00007ffff7da84d7 <+7>: mov $0xfffffffffffffff0,%rcx
0x00007ffff7da84de <+14>: mov %fs:(%rax),%rax
=> 0x00007ffff7da84e2 <+18>: mov (%rax),%rax
0x00007ffff7da84e5 <+21>: mov 0x40(%rax),%rsi
with $rax == 0
. When ld.so
itself goes through this code, $rax
is distinctly non-NULL. Obviously something went wrong during TLS
setup, but what?
It turns out that GLIBC initializes its _dl_phdr
from the AT_PHDR
in the auxiliary vector, then iterates over all Phdr
s to look for one with PT_TLS
type.
If there isn't one, then GLIBC assumes that no TLS
set up is necessary.
When ld.so
runs directly, the kernel-supplied aux vector points to Phdr
s for ld.so
, PT_TLS
is present, and everything works.
But when ld.so
runs indirectly as the interpreter for a.out
, the aux vector points to Phdr
s for a.out
(and not for ld.so
-- this is as designed). Since a.out
doesn't have any thread-local variables, it doesn't have PT_TLS
segment either.
Conclusion: it is currently not possible to build an ELF
interpreter with -static-pie
and GLIBC, unless one is very careful to avoid thread-local storage. And avoiding thread-local storage currently appears to not be an option either: a trivial int main() { return 0; }
still has a TLS
segment despite not using anything at all from GLIBC.
这篇关于为什么将独立的C hello程序用作动态链接器时会崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!