【问题标题】:Why does a standalone C hello program crash when used as a dynamic linker为什么独立的 C hello 程序在用作动态链接器时会崩溃
【发布时间】:2019-09-04 20:51:52
【问题描述】:

以下程序:

#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;
}

内置于静态链接的 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) 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

这是为什么呢?

以上所有地址都在./ld.so 本身内,因此它在自己的初始化过程中崩溃。事实上,自从ld.so 退出后,控件将永远不会到达a.out

【问题讨论】:

    标签: gcc glibc elf dynamic-linking


    【解决方案1】:

    调试时间比我预期的要长。

    崩溃发生在:

    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
    

    $rax == 0。当ld.so 本身通过此代码时,$rax 显然是非 NULL。显然在TLS 设置过程中出了点问题,但是什么?

    事实证明,GLIBC 从辅助向量中的 AT_PHDR 初始化其 _dl_phdr,然后遍历所有 Phdrs 以查找具有 PT_TLS 类型的一个。

    如果没有,则 GLIBC 假定不需要设置 TLS

    ld.so 直接运行时,内核提供的辅助向量指向Phdrs for ld.soPT_TLS 存在,一切正常。

    但是当ld.so 作为a.out 的解释器间接 运行时,辅助向量指向Phdrs 的a.out(而不是ld.so——这就是设计)。由于a.out 没有任何线程局部变量,因此它也没有PT_TLS 段。

    结论:目前不可能用-static-pie 和GLIBC 构建ELF 解释器,除非非常小心地避免线程本地存储。避免线程本地存储目前似乎也不是一种选择:尽管根本没有使用来自 GLIBC 的 任何东西,但一个微不足道的 int main() { return 0; } 仍然有一个 TLS 段。

    【讨论】:

    猜你喜欢
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多