0%

signal 相关整理

android r base

信号相关流程梳理

程序分为两种:
1.静态链接: 由链接器在链接时将库的内容添加到可执行程序中的做法,最大缺点是生成的可执行文件太大,需要更多的系统资源,在装入内存也会消耗更多的时间。
2.动态链接: 将独立的模块先编译成动态库,程序运行有需要它们时才加载,最大缺点是可执行程序依赖分别存储的库文件才能正确执行。
在Android中,大部分都是动态链接,而只有init等少部分是静态链接,因此native程序也是动态链接程序,动态链接程序是需要链接器才能跑起来,liner就是Android的链接器。
liner也是在程序的进程空间内,当内核将应用程序加载起来后,并不是先跑应用程序代码,而是先跑liner。linker负责将应用程序所需的库加载到进程空间内,之后才跑应用程序代码。

绝大多数的native app都是以linker为入口(动态连接的程序),在启动时先由汇编跳转到__link_init函数.

[-> arch/arm64/begin.S]

1
2
3
4
5
6
7
8
9
10
ENTRY(_start)
// Force unwinds to end in this function.
.cfi_undefined x30

mov x0, sp
bl __linker_init

/* linker init returns the _entry address in the main image */
br x0
END(_start)

到debuggerd_init的调用链

1
2
3
4
5
6
7
-- bl __linker_init
| - extern "C" ElfW(Addr) __linker_init(void* raw_args) % bionic/linker/linker_main.cpp
\ - __linker_init_post_relocation(KernelArgumentBlock& args, soinfo& tmp_linker_so)
\ - return start_address = linker_main(args, exe_to_load);
\ - ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load)
\ - linker_debuggerd_init(); % Register the debuggerd signal handler. [ linker_debuggerd_android.cpp ]
\ - debuggerd_init(debuggerd_callbacks_t* callbacks) % [debuggerd/handler/debuggerd_handler.cpp ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void linker_debuggerd_init() {
debuggerd_callbacks_t callbacks = {
.get_abort_message = []() {
return __libc_shared_globals()->abort_msg;
},
.post_dump = &notify_gdb_of_libraries,
.get_gwp_asan_state = []() {
return __libc_shared_globals()->gwp_asan_state;
},
.get_gwp_asan_metadata = []() {
return __libc_shared_globals()->gwp_asan_metadata;
},
};
debuggerd_init(&callbacks);
}

debuggerd_init

所在库 /home/mi/work_space/miui-r-umi-dev/system/core/debuggerd/handler/debuggerd_handler.cpp libdebuggerd_handler_core

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
void debuggerd_init(debuggerd_callbacks_t* callbacks) {
if (callbacks) {
g_callbacks = *callbacks;
}
struct sigaction action;
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask);
2. action.sa_sigaction = debuggerd_signal_handler;
action.sa_flags = SA_RESTART | SA_SIGINFO;

// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
1. debuggerd_register_handlers(&action);
}

1.-> static void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {
char value[PROP_VALUE_MAX] = "";
// 注意属性控制 signal的转发 (ro.debuggable=1 && debug.debuggerd.disable=1) 时关掉, 不由linker进行监控 其他情况下开启监控
bool enabled =
!(__system_property_get("ro.debuggable", value) > 0 && !strcmp(value, "1") &&
__system_property_get("debug.debuggerd.disable", value) > 0 && !strcmp(value, "1"));
if (enabled) {
sigaction(SIGABRT, action, nullptr);
sigaction(SIGBUS, action, nullptr);
sigaction(SIGFPE, action, nullptr);
sigaction(SIGILL, action, nullptr);
sigaction(SIGSEGV, action, nullptr);
sigaction(SIGSTKFLT, action, nullptr);
sigaction(SIGSYS, action, nullptr);
sigaction(SIGTRAP, action, nullptr);
}
// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
// triggered via BIONIC_SIGNAL_DEBUGGER
sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);
}

信号的处理函数为 debuggerd_signal_handler

debuggerd_signal_handler

连接到bionic上的native程序(C/C++)出现异常时,kernel会发送相应的signal; 当进程捕获致命的signal,通知debuggerd调用ptrace来获取有价值的信息(发生crash之前)。

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) {
if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
// Allow for the abort message to be explicitly specified via the sigqueue value.
// Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {
abort_message = reinterpret_cast<void*>(si_val & ~1);
info->si_ptr = reinterpret_cast<void*>(si_val & 1);
}
}
} else {
//传入形参的callback的定义的字段
if (g_callbacks.get_abort_message) {
abort_message = g_callbacks.get_abort_message();
}
if (g_callbacks.get_gwp_asan_state) {
gwp_asan_state = g_callbacks.get_gwp_asan_state();
}
if (g_callbacks.get_gwp_asan_metadata) {
gwp_asan_metadata = g_callbacks.get_gwp_asan_metadata();
}
}

if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
// prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1 条件生效
// A value of 1 indicates execve(2) will operate in the privilege-restricting mode described above
debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), abort_message);
resend_signal(info);
return;
}
/*
* Writes a summary of the signal to the log file. We do this so that, if
* for some reason we're not able to contact debuggerd, there is still some
* indication of the failure in the log.
*/
// 防止无法连接debuggerd 时, 仍可以输出部分crash信息.
log_signal_summary(info);
{
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
"Fatal signal %d (%s), code %d (%s%s)%s in tid %d (%s), pid %d (%s)",
info->si_signo, get_signame(info), info->si_code, get_sigcode(info),
sender_desc, addr_desc, __gettid(), thread_name, self_pid, main_thread_name);
}
debugger_thread_info thread_info = {
.crashing_tid = __gettid(),
.pseudothread_tid = -1,
.siginfo = info,
.ucontext = context,
.abort_msg = reinterpret_cast<uintptr_t>(abort_message),
.fdsan_table = reinterpret_cast<uintptr_t>(android_fdsan_get_fd_table()),
.gwp_asan_state = reinterpret_cast<uintptr_t>(gwp_asan_state),
.gwp_asan_metadata = reinterpret_cast<uintptr_t>(gwp_asan_metadata),
};
// Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.
// Set the state of the "dumpable" flag, which determines whether core dumps are produced for the calling process upon
// delivery of a signal whose default behavior is to produce a core dump.
if (prctl(PR_SET_DUMPABLE, 1) != 0) {
fatal_errno("failed to set dumpable");
}
// PR_SET_PTRACER_ANY,对于调用进程将有效禁用Yama引入的ptrace限制。
if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) {...}
// fork crash_dump 进程, 传入的是线程的信息
pid_t child_pid =
-> clone(debuggerd_dispatch_pseudothread, pseudothread_stack,
CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,
&thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);
// 重置为默认信号处理函数
if (signal_number != DEBUGGER_SIGNAL) {
signal(signal_number, SIG_DFL);
}
if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
// If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
} else {
// Resend the signal, so that either gdb or the parent's waitpid sees it.
// 重发信号, 可以让gdb strace等处理信号
resend_signal(info);
}

}

debuggerd_dispatch_pseudothread

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
45
46
47
static int debuggerd_dispatch_pseudothread(void* arg) { 
// 创建管道, 用于父子进程通信
unique_fd input_read, input_write;
unique_fd output_read, output_write;
if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) {
fatal_errno("failed to create pipe");
}
struct iovec iovs[] = {
{.iov_base = &version, .iov_len = sizeof(version)},
{.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},
{.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
{.iov_base = &thread_info->abort_msg, .iov_len = sizeof(uintptr_t)},
{.iov_base = &thread_info->fdsan_table, .iov_len = sizeof(uintptr_t)},
{.iov_base = &thread_info->gwp_asan_state, .iov_len = sizeof(uintptr_t)},
{.iov_base = &thread_info->gwp_asan_metadata, .iov_len = sizeof(uintptr_t)},
};
ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));
// Don't use fork(2) to avoid calling pthread_atfork handlers. 没有直接使用fork, 而是用的clone函数
pid_t crash_dump_pid = __fork();
{
return clone(nullptr, nullptr, 0, nullptr);
}
// fork 失败
if (crash_dump_pid == -1) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc",
"failed to fork in debuggerd signal handler: %s", strerror(errno));
// It returns 0 in the child process and returns the PID of the child in the parent. 0是子进程 >0是父进程
} else if (crash_dump_pid == 0) {
// STDOUT_FILENO = input_write STDIN_FILENO = output_read
TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO));
TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO));
// 替换为 crash_dump 本体执行, 输入变成output_read,输出变为 input_write, 注意前面的管道, output_write 是父进程用的, 対端即为 output_read, 下面注意input_read的处理
execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
nullptr, nullptr);
}
// 父进程中, 读取子进程的 printf的log
rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));
// crash_dump is ptracing us, fork off a copy of our address space for it to use. ?
create_vm_process();
// 等待crash_dump结束
if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
strerror(errno));
} else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
}
}