为什么将独立的C hello程序用作动态链接器时会崩溃 [英] Why does a standalone C hello program crash when used as a dynamic linker

查看:123
本文介绍了为什么将独立的C hello程序用作动态链接器时会崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下程序:

  #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-in​​fo.c:31
31 * bp =(const uint16_t *)_NL_CURRENT(LC_CTYPE,_NL_CTYPE_CLASS)+ 128;
(gdb)bt
#0 0x00007ffff7da84e2在__ctype_init()在ctype-in​​fo.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构建 ELF 解释器和GLIBC,除非非常小心地避免线程本地存储。并且避免当前也不选择线程本地存储:琐碎的 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 Phdrs 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 Phdrs 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 Phdrs 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程序用作动态链接器时会崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆