说明:本篇是以前学习时的记录,后期进行过整理,有些地方没有完善,请各位师傅见谅。
一个三环程序是始于链接器,终于终止信号,虽然链接器执行的过程在程序运行前的过程很有意思,但对PWN
掉程序而言不如攻击终止程序来的直接。exit
是linux
常见的终止程序,本人在学习的过程中发现了一些可以利用的地方,并将其记录了下来和各位师傅一起学习。模仿ROP(Return-oriented programming
)我将其命名为EOP(Exit-oriented programming
)。
江湖流传的exit_hook
劫持流程是 exit => _dl_fini => __rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive
这两个函数指针在_rtld_global (_rtld_local)
中,通过修改函数指针来劫持执行流。同时,还有一个at_quick_exit
函数,调用initial_quick
。
我在学习的时候发现_dl_fini
是靠 (fs:0x30) ^ (initial:0x18)
(__exit_funcs
存储的内容) 得来,其中 initial:0x20
为参数rdi
,这个过程非常有意思,涉及tcb
结构体,涉及函数的计算,可以看出这个地方以前肯定被别人攻击过,加入了ptr_guard
进行防护,具体内容下面有描述。
名字是一个事务最好解释,在学习库函数过程中,有很多名次简写让人摸不到头脑,所以有必要提前说明一下。
例如:__cxa_atexit
解释:为CXXABI
简写,是C++
语言内部实现的一套标准,其目的是为了确保分开编译的程序模块链接到一起后能够正确工作。所谓C++
语言内部实现标准又是什么意思?该标准规定了C++
对象在内存中是如何布局的、构造函数和析构函数的规则是什么,如何支持异常,如何创建模板实例,命名空间的规则是什么样的、如何支持虚函数等等。所有这些C++
语言内部实现的细节就构成了CXXABI
。
例如:RTLD_LAZY RTLD_GLOBAL
解释:run time
例如:ld_library_path
解释:load
例如:dlopen dlclose
解释: Dynamic linking library
,动态链接库。
例如:# define GLRO(name) _rtld_local_ro._##name
解释:global/local read-only
例如:__do_global_ctors_aux ()
解释:辅助(auxilirary
)。gcc
中aux
属性用于从C直接访问ARC
(architecture variant
变体架构)的辅助寄存器空间。辅助寄存器编号通过属性参数给出。
例如:__do_global_dtors_aux __do_global_ctors_aux
解释:构造函数(constructors
),析构函数(destructors
)
例如:crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o
解释:c runtime
,用于执行进入main
之前的初始化和退出main
之后的扫尾工作。
例如:DT_NUM DT_THISPROCNUM
解释:d_tag (dynamic entry type)
,连接器的类型
exit
部分有用的函数调用过程如下,
没啥好说的,只是传入了__exit_funcs
的地址,它存储的是initial
结构体的地址。
结构体相关内容如下,可以看出它里面保存了32个函数的地址及参数,当然如果不够用可以用链表进行串联。
__run_exit_handlers
属于exit
的主要函数,内容挺多,我们一步一步说
上面的枚举很简单就是从0到4,
一般initial
结构体内容如下,可以看出flavor
为4,也就是一直执行cxafct(arg, status)
。
此函数的作用使tls
析构,是执行tls_dtor_list
结构体中的函数,并且参数也在其中。通常结构体为空,判断一下就出来了。此部分内容涉及线程结构体,linux
中要用到fs
寄存器。
从上面可以看出 cxafct
显然不是一个正常的内存地址,是通过PTR_DEMANGLE (cxafct);
之后才成为正常函数的。但这里我有点疑问,因为我查到的原码是循环右移9位,然而真实是循环右移11位??????????????????可能与Makefile
配置相关,有待再去看详细过程。
tcbhead_t
结构体如下
从上面可以看出, _dl_fini
函数地址的产生过程为pointer_guard (fs:0x30) ^ cxafct (initial:0x18)
,如果我们能泄露initial
的内容就能反算出 pointer_guard
,然后再将initial:0x18
处的值就能执行任意函数,参数虽然最多只能有1个,既rdi
,但配合house of 一骑当千
使用仍然可以绕过沙盒。
此宏非常复杂,如果跟踪宏定义可能进入一个陷阱,我们从从libc
的反编译中进行观察,是顺序__libc_atexit
节里面的指针,2.31的汇编如下,其中,__elf_set___libc_atexit_element__IO_cleanup__
里面存的就是 _IO_cleanup
的地址。注意:此部分内容2.35之后已不可写。
执行 _IO_cleanup
**说明:**现在的版本中__libc_atexit
节为虚表的结尾。
_dl_fini
函数没有符号表,需要通过交叉引用才能找到。通过链接器的start
可以快速定位,既ld.so
的start
程序,源代码如下
反编译定位如下
这个函数的说明很有意思,主要是考虑dlopen
的问题,我就不删了。里面很多GL()
其实就是调用_rtld_local
,这个也是一个疑问**我没找到_rtld_local
的定义???**在gdb调试中也将_rtld_local
视为_rtld_global
,暂时就这样看吧。
我本来想将__rtld_lock_lock_recursive
改为gets
,再将GL(dl_load_lock)
处的值修改为自己想要的,然后将__rtld_lock_unlock_recursive
改为setcontext+53
就可以控制执行流,但实际操作中 GL(dl_nns)
等于1,就要执行一通操作,并且涉及link_map
。
_ns_nloaded
一般为4,同时 _ns_loaded
存储的是文件的基地址。
_rtld_global
结构体每一代版本都不同,以下是 glibc-2.31
。这个结构体是链接器的起始鼻祖,可以说是包罗万象,其中最重要的就是涉及所有文件的内存地址。例如:
在_dl_rtld_map
结构体中,l_name
存储的是链接器名称所在的地址,因为链接器名字必然写在文件中,所以可以计算出文件的地址。
__exit_funcs
没有符号表,需要手动查找偏移,通过exit
可以快速定位。
攻击__exit_funcs
必须要结合攻击initial
,因为要反算pointer_guard
,除非能利用泄露tcb
结构体数据。攻击思路如下。
泄露 __exit_funcs 和 initial
=> 计算pointer_guard
=> 修改initial
的内容,或者将__exit_funcs
的内容改到堆上。
1.__exit_funcs
附近数据非常丰富,可以使用house of spirit
等方式,在不能使用tcache
时可以考虑。
2.双堆题目时,将__exit_funcs
作为指向数据块的指针,此时能较为方便泄露initial
。那么再次申请一个堆块伪造initial
就水到渠成。
3.对于2.32以前的版本时可以使用tcache
直连续2次接申请到initial
处。
1.攻击的核心还是伪造initial
,不如直接攻击initial
来的方便。
2.参数只能有1个,绕过沙盒需要借助house of 一骑当千
。
3.需要泄露堆地址。
initial
也是没有符号表,需要手动查找偏移,利用__exit_funcs
可以快速定位。
攻击initial
相对较为简单,也需要反算pointer_guard
,除非能利用泄露tcb
结构体数据。攻击思路如下,显然攻击思路要简单很多
泄露 initial
=> 计算pointer_guard
=> 修改initial
的内容。
1.攻击步骤简单
2.可以使用多函数调用,需要注意的是调用过程是从后向前调用。利用getcontext + gets + gets + setcontext
执行 mproject + shellcode
1.能控制initial
的方法太少了,它附近没有什么数据可以利用,只能使用tcache
。
2.34后 __rtld_lock_lock_recursive 和__rtld_lock_unlock_recursive
不能攻击,_IO_cleanup
是另外一个思路。注意:2.35之后_IO_cleanup
已不可写
不用 ld 文件就可以确定地址
只能使用 one_gadget
,无法控制参数。
_dl_fini
是靠 (fs:0x30) ^ (initial:0x18) (__exit_funcs
存储的内容) 得来,其中 (initial:0x20) 为参数(rdi)
。那么我们可以有以下攻击思路。
1.修改 initial 结构体的值
2.将 __exit_funcs
的值改在堆上 (house of spirit)
其实,目前攻击_dl_fini
就是攻击_rtld_global
,它不能直接泄露,需要有二次泄露的机会。
对于2.32以前的版本时可以使用tcache
直连续2次接申请到_rtld_global
处。
没啥好说的,通用技能
1.__rtld_lock_define_recursive (EXTERN, _dl_load_lock)
的地址是这两个函数的参数,只有一个字长,不能写入太多数据,写入太多会影响后面程序执行。
2.2.34 已无法使用
这种攻击方式与unlink
结合简直是绝配。因为_dl_rtld_map.ld_name
存储的就是链接器字符串所在的地址,将其泄露后可以计算出file
的地址,此时就能利用unlink
申请到任一内存。注意:由于不能劫持执行流,后续的攻击还要结合其他方式。
1.可以让攻击思路异常简单。
2._dl_rtld_map
附近数据非常丰富,各种手段都能够攻击到这里。
1.在没有ld
文件的情况下要泄露 3 次
2.LOAD
节末尾一般都是 00,所以puts
泄漏时需要注意
3.free
的次数需要足够多。
4.攻击过程非常繁琐。
以下代码主要是检测l->l_next
,所以伪造的_ns_loaded
需要回复原来的l->l_next
,实际中通过 _rtld_global._dl_ns._ns_loaded.l_next
计算偏移
下面一共有3个检测
能够执行多个函数
不能控制参数,绕过沙盒比较复杂
还有一种攻击方式是能多次执行__rtld_lock_lock_recursive 和 __rtld_lock_unlock_recursive
,由于这三个数据距离非常远,攻击步骤就显得非常繁琐,除非将_rtld_global
全部改写。实用性不高,因为太繁琐所以没测试。
以上的攻击中都存在一个问题,因为攻击的函数都是指针,参数数量无法改变,除了个别可以使用house of 一骑当千
,其他考虑使用多步的攻击方进行。
题目如果不给出ld
文件,则无法直接确定 _rtld_local
地址,可以在libc
中找到libc.symbols['_rtld_global_ptr']
找到,但需要一次泄露,对于2.32以前的版本时可以使用tcache
直连续2次接申请到_rtld_global
处。
每个版本libc
中 _rtld_local
结构体可能会有变化,需要具体问题具体分析。
没有 ld
文件也就不知道 _dl_fini
的地址。
可以结合攻击environ
泄露栈地址。
EOP
对泄露次数要求较高。
在我退役前,很多题目都是直接调用__exit
函数,所以很正的使用机会很少,以下板子使用频率并不高。
ptr_guard
是攻击initial
的关键,主要有以下两个函数
EOP
之后我才发现house of 一骑当千
的用法,所以上面 getcontext + gets + gets + setcontext
可以简化为 gets + setcontext
。
在我退役前,很多题目都是直接调用__exit
函数,所以很正的使用机会很少。下面以2022巅峰极客 happy_note
为例。
一次UAF
2次malloc
比赛时我还不会house of apple
,使用EOP
处理的。
醋打哪酸,盐打哪咸,西瓜怎么那么甜,大蒜青椒哪个辣,何物苦又赛黄连
段有何用,节在哪空,程序何时才启动,动静加载因何改,金丝雀怎样飞入宫。
醋打哪酸,盐打哪咸,西瓜怎么那么甜,大蒜青椒哪个辣,何物苦又赛黄连
段有何用,节在哪空,程序何时才启动,动静加载因何改,金丝雀怎样飞入宫。
https:
/
/
sourceware.org
/
legacy
-
ml
/
libc
-
help
/
2012
-
03
/
msg00006.html
The GLRO() macro
is
used to access
global
or
local read
-
only data, see sysdeps
/
generic
/
ldsodefs.h.
https:
/
/
sourceware.org
/
legacy
-
ml
/
libc
-
help
/
2012
-
03
/
msg00006.html
The GLRO() macro
is
used to access
global
or
local read
-
only data, see sysdeps
/
generic
/
ldsodefs.h.
exit
=
>
__run_exit_handlers(status, &__exit_funcs, true, true)
=
>
__call_tls_dtors
_dl_fini(void)
=
>
_dl_rtld_lock_recursive (函数及参数均在 _rtld_global 中,下同)
_dl_rtld_unlock_recursive
_IO_cleanup
_exit (系统调用封装)
exit
=
>
__run_exit_handlers(status, &__exit_funcs, true, true)
=
>
__call_tls_dtors
_dl_fini(void)
=
>
_dl_rtld_lock_recursive (函数及参数均在 _rtld_global 中,下同)
_dl_rtld_unlock_recursive
_IO_cleanup
_exit (系统调用封装)
void
exit
(
int
status)
{
__run_exit_handlers (status, &__exit_funcs,
true
,
true
);
}
libc_hidden_def (
exit
)
void
exit
(
int
status)
{
__run_exit_handlers (status, &__exit_funcs,
true
,
true
);
}
libc_hidden_def (
exit
)
static
struct
exit_function_list initial;
struct
exit_function_list *__exit_funcs = &initial;
struct
exit_function_list
{
struct
exit_function_list *next;
size_t
idx;
struct
exit_function fns[32];
};
struct
exit_function
{
long
int
flavor;
union
{
void
(*at) (
void
);
struct
{
void
(*fn) (
int
status,
void
*arg);
void
*arg;
} on;
struct
{
void
(*fn) (
void
*arg,
int
status);
void
*arg;
void
*dso_handle;
} cxa;
} func;
};
static
struct
exit_function_list initial;
struct
exit_function_list *__exit_funcs = &initial;
struct
exit_function_list
{
struct
exit_function_list *next;
size_t
idx;
struct
exit_function fns[32];
};
struct
exit_function
{
long
int
flavor;
union
{
void
(*at) (
void
);
struct
{
void
(*fn) (
int
status,
void
*arg);
void
*arg;
} on;
struct
{
void
(*fn) (
void
*arg,
int
status);
void
*arg;
void
*dso_handle;
} cxa;
} func;
};
void
attribute_hidden __run_exit_handlers (
int
status,
struct
exit_function_list **listp,
bool
run_list_atexit,
bool
run_dtors)
{
#ifndef SHARED
if
(&__call_tls_dtors != NULL)
#endif
if
(run_dtors)
__call_tls_dtors ();
__libc_lock_lock (__exit_funcs_lock);
while
(
true
)
{
struct
exit_function_list *cur = *listp;
if
(cur == NULL)
{
__exit_funcs_done =
true
;
break
;
}
while
(cur->idx > 0)
{
struct
exit_function *
const
f = &cur->fns[--cur->idx];
const
uint64_t new_exitfn_called = __new_exitfn_called;
switch
(f->flavor)
{
void
(*atfct) (
void
);
void
(*onfct) (
int
status,
void
*arg);
void
(*cxafct) (
void
*arg,
int
status);
void
*arg;
case
ef_free:
case
ef_us:
break
;
case
ef_on:
onfct = f->func.on.fn;
arg = f->func.on.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (onfct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
onfct (status, arg);
__libc_lock_lock (__exit_funcs_lock);
break
;
case
ef_at:
atfct = f->func.at;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (atfct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
atfct ();
__libc_lock_lock (__exit_funcs_lock);
break
;
case
ef_cxa:
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
arg = f->func.cxa.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (cxafct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
cxafct (arg, status);
__libc_lock_lock (__exit_funcs_lock);
break
;
}
if
(__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
continue
;
}
*listp = cur->next;
if
(*listp != NULL)
free
(cur);
}
__libc_lock_unlock (__exit_funcs_lock);
if
(run_list_atexit)
RUN_HOOK (__libc_atexit, ());
_exit (status);
}
void
attribute_hidden __run_exit_handlers (
int
status,
struct
exit_function_list **listp,
bool
run_list_atexit,
bool
run_dtors)
{
#ifndef SHARED
if
(&__call_tls_dtors != NULL)
#endif
if
(run_dtors)
__call_tls_dtors ();
__libc_lock_lock (__exit_funcs_lock);
while
(
true
)
{
struct
exit_function_list *cur = *listp;
if
(cur == NULL)
{
__exit_funcs_done =
true
;
break
;
}
while
(cur->idx > 0)
{
struct
exit_function *
const
f = &cur->fns[--cur->idx];
const
uint64_t new_exitfn_called = __new_exitfn_called;
switch
(f->flavor)
{
void
(*atfct) (
void
);
void
(*onfct) (
int
status,
void
*arg);
void
(*cxafct) (
void
*arg,
int
status);
void
*arg;
case
ef_free:
case
ef_us:
break
;
case
ef_on:
onfct = f->func.on.fn;
arg = f->func.on.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (onfct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
onfct (status, arg);
__libc_lock_lock (__exit_funcs_lock);
break
;
case
ef_at:
atfct = f->func.at;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (atfct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
atfct ();
__libc_lock_lock (__exit_funcs_lock);
break
;
case
ef_cxa:
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
arg = f->func.cxa.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (cxafct);
#endif
__libc_lock_unlock (__exit_funcs_lock);
cxafct (arg, status);
__libc_lock_lock (__exit_funcs_lock);
break
;
}
if
(__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
continue
;
}
*listp = cur->next;
if
(*listp != NULL)
free
(cur);
}
__libc_lock_unlock (__exit_funcs_lock);
if
(run_list_atexit)
RUN_HOOK (__libc_atexit, ());
_exit (status);
}
enum
{
ef_free,
ef_us,
ef_on,
ef_at,
ef_cxa
};
enum
{
ef_free,
ef_us,
ef_on,
ef_at,
ef_cxa
};
struct
dtor_list
{
dtor_func func;
void
*obj;
struct
link_map *map;
struct
dtor_list *next;
};
static
__thread
struct
dtor_list *tls_dtor_list;
void
__call_tls_dtors (
void
)
{
while
(tls_dtor_list)
{
struct
dtor_list *cur = tls_dtor_list;
dtor_func func = cur->func;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (func);
#endif
tls_dtor_list = tls_dtor_list->next;
func (cur->obj);
atomic_fetch_add_release (&cur->map->l_tls_dtor_count, -1);
free
(cur);
}
}
libc_hidden_def (__call_tls_dtors)
struct
dtor_list
{
dtor_func func;
void
*obj;
struct
link_map *map;
struct
dtor_list *next;
};
static
__thread
struct
dtor_list *tls_dtor_list;
void
__call_tls_dtors (
void
)
{
while
(tls_dtor_list)
{
struct
dtor_list *cur = tls_dtor_list;
dtor_func func = cur->func;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (func);
#endif
tls_dtor_list = tls_dtor_list->next;
func (cur->obj);
atomic_fetch_add_release (&cur->map->l_tls_dtor_count, -1);
free
(cur);
}
}
libc_hidden_def (__call_tls_dtors)
#if IS_IN (rtld)
#else
# ifdef __ASSEMBLER__
# define PTR_MANGLE(reg) xorl %gs:POINTER_GUARD, reg; \
roll $9, reg
# define PTR_DEMANGLE(reg) rorl $9, reg; \
xorl %gs:POINTER_GUARD, reg
# else
# define PTR_MANGLE(var) asm ("xorl %%gs:%c2, %0\n" \
"roll $9, %0"
\
:
"=r"
(var) \
:
"0"
(var), \
"i"
(offsetof (tcbhead_t, \
pointer_guard)))
# define PTR_DEMANGLE(var) asm ("rorl $9, %0\n" \
"xorl %%gs:%c2, %0"
\
:
"=r"
(var) \
:
"0"
(var), \
"i"
(offsetof (tcbhead_t, \
pointer_guard)))
# endif
#endif
#if IS_IN (rtld)
#else
# ifdef __ASSEMBLER__
# define PTR_MANGLE(reg) xorl %gs:POINTER_GUARD, reg; \
roll $9, reg
# define PTR_DEMANGLE(reg) rorl $9, reg; \
xorl %gs:POINTER_GUARD, reg
# else
# define PTR_MANGLE(var) asm ("xorl %%gs:%c2, %0\n" \
"roll $9, %0"
\
:
"=r"
(var) \
:
"0"
(var), \
"i"
(offsetof (tcbhead_t, \
pointer_guard)))
# define PTR_DEMANGLE(var) asm ("rorl $9, %0\n" \
"xorl %%gs:%c2, %0"
\
:
"=r"
(var) \
:
"0"
(var), \
"i"
(offsetof (tcbhead_t, \
pointer_guard)))
# endif
#endif
typedef
struct
{
void
*tcb;
dtv_t *dtv;
void
*self;
int
multiple_threads;
uintptr_t
sysinfo;
uintptr_t
stack_guard;
uintptr_t
pointer_guard;
int
gscope_flag;
#ifndef __ASSUME_PRIVATE_FUTEX
int
private_futex;
#else
int
__glibc_reserved1;
#endif
void
*__private_tm[4];
void
*__private_ss;
} tcbhead_t;
typedef
struct
{
void
*tcb;
dtv_t *dtv;
void
*self;
int
multiple_threads;
uintptr_t
sysinfo;
uintptr_t
stack_guard;
uintptr_t
pointer_guard;
int
gscope_flag;
#ifndef __ASSUME_PRIVATE_FUTEX
int
private_futex;
#else
int
__glibc_reserved1;
#endif
void
*__private_tm[4];
void
*__private_ss;
} tcbhead_t;
int
_IO_cleanup (
void
)
{
int
result = _IO_flush_all_lockp (0);
_IO_unbuffer_all ();
return
result;
}
int
_IO_cleanup (
void
)
{
int
result = _IO_flush_all_lockp (0);
_IO_unbuffer_all ();
return
result;
}
#ifdef RTLD_START
RTLD_START
#else
# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
#endif
#define RTLD_START asm ("\n\
.text\n\
.align 16\n\
.globl _start\n\
.globl _dl_start_user\n\
_start:\n\
movq %rsp, %rdi\n\
call _dl_start\n\
_dl_start_user:\n\
# Save the user entry point address in %r12.\n\
movq %rax, %r12\n\
# See if we were run as a command with the executable file\n\
# name as an extra leading argument.\n\
movl _dl_skip_args(%rip), %eax\n\
# Pop the original argument count.\n\
popq %rdx\n\
# Adjust the stack pointer to skip _dl_skip_args words.\n\
leaq (%rsp,%rax,8), %rsp\n\
# Subtract _dl_skip_args from argc.\n\
subl %eax, %edx\n\
# Push argc back on the stack.\n\
pushq %rdx\n\
# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env)\n\
# argc -> rsi\n\
movq %rdx, %rsi\n\
# Save %rsp value in %r13.\n\
movq %rsp, %r13\n\
# And align stack for the _dl_init call. \n\
andq $-16, %rsp\n\
# _dl_loaded -> rdi\n\
movq _rtld_local(%rip), %rdi\n\
# env -> rcx\n\
leaq 16(%r13,%rdx,8), %rcx\n\
# argv -> rdx\n\
leaq 8(%r13), %rdx\n\
# Clear %rbp to mark outermost frame obviously even for constructors.\n\
xorl %ebp, %ebp\n\
# Call the function to run the initializers.\n\
call _dl_init\n\
# Pass our finalizer function to the user in %rdx, as per ELF ABI.\n\
leaq _dl_fini(%rip), %rdx\n\
# And make sure %rsp points to argc stored on the stack.\n\
movq %r13, %rsp\n\
# Jump to the user's entry point.\n\
jmp *%r12\n\
.previous\n\
");
#ifdef RTLD_START
RTLD_START
#else
# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
#endif
#define RTLD_START asm ("\n\
.text\n\
.align 16\n\
.globl _start\n\
.globl _dl_start_user\n\
_start:\n\
movq %rsp, %rdi\n\
call _dl_start\n\
_dl_start_user:\n\
# Save the user entry point address in %r12.\n\
movq %rax, %r12\n\
# See if we were run as a command with the executable file\n\
# name as an extra leading argument.\n\
movl _dl_skip_args(%rip), %eax\n\
# Pop the original argument count.\n\
popq %rdx\n\
# Adjust the stack pointer to skip _dl_skip_args words.\n\
leaq (%rsp,%rax,8), %rsp\n\
# Subtract _dl_skip_args from argc.\n\
subl %eax, %edx\n\
# Push argc back on the stack.\n\
pushq %rdx\n\
# Call _dl_init (struct link_map *main_map, int argc, char **argv, char **env)\n\
# argc -> rsi\n\
movq %rdx, %rsi\n\
# Save %rsp value in %r13.\n\
movq %rsp, %r13\n\
# And align stack for the _dl_init call. \n\
andq $-16, %rsp\n\
# _dl_loaded -> rdi\n\
movq _rtld_local(%rip), %rdi\n\
# env -> rcx\n\
leaq 16(%r13,%rdx,8), %rcx\n\
# argv -> rdx\n\
leaq 8(%r13), %rdx\n\
# Clear %rbp to mark outermost frame obviously even for constructors.\n\
xorl %ebp, %ebp\n\
# Call the function to run the initializers.\n\
call _dl_init\n\
# Pass our finalizer function to the user in %rdx, as per ELF ABI.\n\
leaq _dl_fini(%rip), %rdx\n\
# And make sure %rsp points to argc stored on the stack.\n\
movq %r13, %rsp\n\
# Jump to the user's entry point.\n\
jmp *%r12\n\
.previous\n\
");
void
_dl_fini (
void
)
{
#ifdef SHARED
int
do_audit = 0;
again:
#endif
for
(Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned
int
nloaded = GL(dl_ns)[ns]._ns_nloaded;
if
(nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
__rtld_lock_unlock_recursive (GL(dl_load_lock));
else
{
#ifdef SHARED
_dl_audit_activity_nsid (ns, LA_ACT_DELETE);
#endif
struct
link_map *maps[nloaded];
unsigned
int
i;
struct
link_map *l;
assert
(nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
for
(l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
if
(l == l->l_real)
{
assert
(i < nloaded);
maps[i] = l;
l->l_idx = i;
++i;
++l->l_direct_opencount;
}
assert
(ns != LM_ID_BASE || i == nloaded);
assert
(ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
unsigned
int
nmaps = i;
_dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE),
true
);
__rtld_lock_unlock_recursive (GL(dl_load_lock));
for
(i = 0; i < nmaps; ++i)
{
struct
link_map *l = maps[i];
if
(l->l_init_called)
{
l->l_init_called = 0;
if
(l->l_info[DT_FINI_ARRAY] != NULL
|| (ELF_INITFINI && l->l_info[DT_FINI] != NULL))
{
if
(__builtin_expect (GLRO(dl_debug_mask)
& DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf (
"\ncalling fini: %s [%lu]\n\n"
,
DSO_FILENAME (l->l_name),
ns);
if
(l->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (l->l_addr
+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned
int
i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/
sizeof
(ElfW(Addr)));
while
(i-- > 0)
((fini_t) array[i]) ();
}
if
(ELF_INITFINI && l->l_info[DT_FINI] != NULL)
DL_CALL_DT_FINI
(l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
}
#ifdef SHARED
_dl_audit_objclose (l);
#endif
}
--l->l_direct_opencount;
}
#ifdef SHARED
_dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
#endif
}
}
#ifdef SHARED
if
(! do_audit && GLRO(dl_naudit) > 0)
{
do_audit = 1;
goto
again;
}
if
(__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
_dl_debug_printf (
"\nruntime linker statistics:\n"
" final number of relocations: %lu\n"
"final number of relocations from cache: %lu\n"
,
GL(dl_num_relocations),
GL(dl_num_cache_relocations));
#endif
}
void
_dl_fini (
void
)
{
#ifdef SHARED
int
do_audit = 0;
again:
#endif
for
(Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned
int
nloaded = GL(dl_ns)[ns]._ns_nloaded;
if
(nloaded == 0
#ifdef SHARED
|| GL(dl_ns)[ns]._ns_loaded->l_auditing != do_audit
#endif
)
__rtld_lock_unlock_recursive (GL(dl_load_lock));
else
{
#ifdef SHARED
_dl_audit_activity_nsid (ns, LA_ACT_DELETE);
#endif
struct
link_map *maps[nloaded];
unsigned
int
i;
struct
link_map *l;
assert
(nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
for
(l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
if
(l == l->l_real)
{
assert
(i < nloaded);
maps[i] = l;
l->l_idx = i;
++i;
++l->l_direct_opencount;
}
assert
(ns != LM_ID_BASE || i == nloaded);
assert
(ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
unsigned
int
nmaps = i;
_dl_sort_maps (maps, nmaps, (ns == LM_ID_BASE),
true
);
__rtld_lock_unlock_recursive (GL(dl_load_lock));
for
(i = 0; i < nmaps; ++i)
{
struct
link_map *l = maps[i];
if
(l->l_init_called)
{
l->l_init_called = 0;
if
(l->l_info[DT_FINI_ARRAY] != NULL
|| (ELF_INITFINI && l->l_info[DT_FINI] != NULL))
{
if
(__builtin_expect (GLRO(dl_debug_mask)
& DL_DEBUG_IMPCALLS, 0))
_dl_debug_printf (
"\ncalling fini: %s [%lu]\n\n"
,
DSO_FILENAME (l->l_name),
ns);
if
(l->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (l->l_addr
+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned
int
i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/
sizeof
(ElfW(Addr)));
while
(i-- > 0)
((fini_t) array[i]) ();
}
if
(ELF_INITFINI && l->l_info[DT_FINI] != NULL)
DL_CALL_DT_FINI
(l, l->l_addr + l->l_info[DT_FINI]->d_un.d_ptr);
}
#ifdef SHARED
_dl_audit_objclose (l);
#endif
}
--l->l_direct_opencount;
}
#ifdef SHARED
_dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
#endif
}
}
#ifdef SHARED
if
(! do_audit && GLRO(dl_naudit) > 0)
{
do_audit = 1;
goto
again;
}
if
(__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
_dl_debug_printf (
"\nruntime linker statistics:\n"
" final number of relocations: %lu\n"
"final number of relocations from cache: %lu\n"
,
GL(dl_num_relocations),
GL(dl_num_cache_relocations));
#endif
}
struct
rtld_global
{
#endif
#ifdef SHARED
# define DL_NNS 16
#else
# define DL_NNS 1
#endif
EXTERN
struct
link_namespaces
{
struct
link_map *_ns_loaded;
unsigned
int
_ns_nloaded;
struct
r_scope_elem *_ns_main_searchlist;
unsigned
int
_ns_global_scope_alloc;
unsigned
int
_ns_global_scope_pending_adds;
struct
unique_sym_table
{
__rtld_lock_define_recursive (, lock)
struct
unique_sym
{
uint32_t hashval;
const
char
*name;
const
ElfW(Sym) *sym;
const
struct
link_map *map;
} *entries;
size_t
size;
size_t
n_elements;
void
(*
free
) (
void
*);
} _ns_unique_sym_table;
struct
r_debug _ns_debug;
} _dl_ns[DL_NNS];
EXTERN
size_t
_dl_nns;
__rtld_lock_define_recursive (EXTERN, _dl_load_lock)
__rtld_lock_define_recursive (EXTERN, _dl_load_write_lock)
EXTERN unsigned
long
long
_dl_load_adds;
EXTERN
struct
link_map *_dl_initfirst;
EXTERN
struct
link_map *_dl_profile_map;
EXTERN unsigned
long
int
_dl_num_relocations;
EXTERN unsigned
long
int
_dl_num_cache_relocations;
EXTERN
struct
r_search_path_elem *_dl_all_dirs;
EXTERN
struct
link_map _dl_rtld_map;
#ifdef SHARED
struct
auditstate _dl_rtld_auditstate[DL_NNS];
#endif
#if defined SHARED && defined _LIBC_REENTRANT \
&& defined __rtld_lock_default_lock_recursive
EXTERN
void
(*_dl_rtld_lock_recursive) (
void
*);
EXTERN
void
(*_dl_rtld_unlock_recursive) (
void
*);
#endif
#define PROCINFO_DECL
#ifndef PROCINFO_CLASS
# define PROCINFO_CLASS EXTERN
#endif
#include <dl-procruntime.c>
EXTERN
int
(*_dl_make_stack_executable_hook) (
void
**);
EXTERN ElfW(Word) _dl_stack_flags;
EXTERN
bool
_dl_tls_dtv_gaps;
EXTERN
size_t
_dl_tls_max_dtv_idx;
EXTERN
struct
dtv_slotinfo_list
{
size_t
len;
struct
dtv_slotinfo_list *next;
struct
dtv_slotinfo
{
size_t
gen;
struct
link_map *map;
} slotinfo[];
} *_dl_tls_dtv_slotinfo_list;
EXTERN
size_t
_dl_tls_static_nelem;
EXTERN
size_t
_dl_tls_static_size;
EXTERN
size_t
_dl_tls_static_used;
EXTERN
size_t
_dl_tls_static_align;
#define TLS_SLOTINFO_SURPLUS (62)
#define DTV_SURPLUS (14)
EXTERN
void
*_dl_initial_dtv;
EXTERN
size_t
_dl_tls_generation;
EXTERN
void
(*_dl_init_static_tls) (
struct
link_map *);
EXTERN
void
(*_dl_wait_lookup_done) (
void
);
EXTERN
struct
dl_scope_free_list
{
size_t
count;
void
*list[50];
} *_dl_scope_free_list;
#if !THREAD_GSCOPE_IN_TCB
EXTERN
int
_dl_thread_gscope_count;
#endif
#ifdef SHARED
};
# define __rtld_global_attribute__
# if IS_IN (rtld)
# ifdef HAVE_SDATA_SECTION
# define __rtld_local_attribute__ \
__attribute__ ((visibility (
"hidden"
), section (
".sdata"
)))
# undef __rtld_global_attribute__
# define __rtld_global_attribute__ __attribute__ ((section (".sdata")))
# else
# define __rtld_local_attribute__ __attribute__ ((visibility ("hidden")))
# endif
extern
struct
rtld_global _rtld_local __rtld_local_attribute__;
# undef __rtld_local_attribute__
# endif
extern
struct
rtld_global _rtld_global __rtld_global_attribute__;
# undef __rtld_global_attribute__
#endif
struct
rtld_global
{
#endif
#ifdef SHARED
# define DL_NNS 16
#else
# define DL_NNS 1
#endif
EXTERN
struct
link_namespaces
{
struct
link_map *_ns_loaded;
unsigned
int
_ns_nloaded;
struct
r_scope_elem *_ns_main_searchlist;
unsigned
int
_ns_global_scope_alloc;
unsigned
int
_ns_global_scope_pending_adds;
struct
unique_sym_table
{
__rtld_lock_define_recursive (, lock)
struct
unique_sym
{
uint32_t hashval;
const
char
*name;
const
ElfW(Sym) *sym;
const
struct
link_map *map;
} *entries;
size_t
size;
size_t
n_elements;
void
(*
free
) (
void
*);
} _ns_unique_sym_table;
struct
r_debug _ns_debug;
} _dl_ns[DL_NNS];
EXTERN
size_t
_dl_nns;
__rtld_lock_define_recursive (EXTERN, _dl_load_lock)
__rtld_lock_define_recursive (EXTERN, _dl_load_write_lock)
EXTERN unsigned
long
long
_dl_load_adds;
EXTERN
struct
link_map *_dl_initfirst;
EXTERN
struct
link_map *_dl_profile_map;
EXTERN unsigned
long
int
_dl_num_relocations;
EXTERN unsigned
long
int
_dl_num_cache_relocations;
EXTERN
struct
r_search_path_elem *_dl_all_dirs;
EXTERN
struct
link_map _dl_rtld_map;
#ifdef SHARED
struct
auditstate _dl_rtld_auditstate[DL_NNS];
#endif
#if defined SHARED && defined _LIBC_REENTRANT \
&& defined __rtld_lock_default_lock_recursive
EXTERN
void
(*_dl_rtld_lock_recursive) (
void
*);
EXTERN
void
(*_dl_rtld_unlock_recursive) (
void
*);
#endif
#define PROCINFO_DECL
#ifndef PROCINFO_CLASS
# define PROCINFO_CLASS EXTERN
#endif
#include <dl-procruntime.c>
EXTERN
int
(*_dl_make_stack_executable_hook) (
void
**);
EXTERN ElfW(Word) _dl_stack_flags;
EXTERN
bool
_dl_tls_dtv_gaps;
EXTERN
size_t
_dl_tls_max_dtv_idx;
EXTERN
struct
dtv_slotinfo_list
{
size_t
len;
struct
dtv_slotinfo_list *next;
struct
dtv_slotinfo
{
size_t
gen;
struct
link_map *map;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!