전 리눅스 커널 및 드라이버 코드를 주로 보는데요. 그런데 리눅스 시스템 프로그램 코드가 자주 봐야 해요.
 
아 그리고 userspace에서 tombstone(무덤)이 떨어지면서 크래시가 종종 발생하거든요. 이런 이슈도 잡아야 되요.
에러 시그니처는 아래와 같아요. 흠...
 
Revision: '0'
ABI: 'arm'
pid: 1558, tid: 1891, name: RenderThread >>> com.google.launcher2 <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 80808080 r2 00000000 r3 00000000
r4 9a979f0c r5 9e7e474c r6 00000000 r7 00000000
backtrace:
#00 pc 00016198 /system/lib/libc.so (strlen+33)
#01 pc 0025b531 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#02 pc 0025b5cb /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#03 pc 0025511f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#04 pc 00240fb7 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#05 pc 00214d67 /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#06 pc 00212f1f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
#07 pc 0021305f /system/app/WebViewGoogle/lib/arm/libwebviewchromium.so
 
이럴 때는 보통 HAL(Hardware Adaptation Layer) 혹은 데몬 코드 리뷰를 하면서 문제점을 점검하며 크래시를 잡는데요.
 
그런데 위와 같이 유저 공간에서 크래시가 발생할 때 커널 패닉을 유발시켜서 코어 덤프를 뜨고 싶을 때가 있어요.
어떤 경우 이런 짓을 하고 싶을까요?
 
뭐 mmap으로 특정 하드웨어를 제어하는 시스템 콜 이후 tombstone이 떨어지던가,
아니면 같은 시간대에 커널 로그를 보고 싶은 경우가 가끔 있어요.
 
아주 가끔은 T32 잘 쓰는 자랑질을 하기 위해서, 
T32 장비를 타겟 디바이스에 연결 후 T32로 break point를 걸고 유저 공간까지 콜스택 까지 볼 수도 있죠.
 
자, 일단 커널 Config를 아래와 같이 설정하고요.
 
diff --git a/arch/arm/configs/pompeii_defconfig b/arch/arm/configs/pompeii_defconfig
index ed1d990..4065c00 100644
--- a/arch/arm/configs/pompeii_defconfig
+++ b/arch/arm/configs/pompeii_defconfig
@@ -777,6 +777,7 @@ CONFIG_DEBUG_SET_MODULE_RONX=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_NETWORK=y
+CONFIG_DEBUG_USER=y
 CONFIG_DYNAMIC_FTRACE=y
 
부트로더에서 "user_debug=24"로 커널 커맨드 라인을 전달해요.
 static void target_common_update_static_bootcmd(void)
 {
+    bootcmd_add_pair("user_debug","24");
+
 
그러면 커널 부팅 시에 아래 코드에서 user_debug 변수를 24로 설정하죠.
[kernel/arch/arm/kernel/traps.c]
#ifdef CONFIG_DEBUG_USER
unsigned int user_debug;
 
static int __init user_debug_setup(char *str)
{
get_option(&str, &user_debug);
return 1;
}
__setup("user_debug=", user_debug_setup);
#endif
 
user_debug 변수를 24로 왜 설정하냐구요? 아래 매크로를 모두 OR 연산 해보면 알 수 있죠.
#define UDBG_UNDEFINED (1 << 0)
#define UDBG_SYSCALL (1 << 1)
#define UDBG_BADABORT (1 << 2)
#define UDBG_SEGV (1 << 3)
#define UDBG_BUS (1 << 4)
 
 
이제 마지막으로, 커널이 부팅하고 나면, 아래와 같이 print-fatal-signals 파일을 1로 설정합니다.
echo 1 > /proc/sys/kernel/print-fatal-signals
 
이제 끝...
 
100% 유저 스페이스에서 tombstone이 떨어지는 디바이스에서 위 설정을 적용하고 실행해보았더니, 아래와 같이 바로 커널 패닉으로 떨어지네요.
참 친절하게도 유저 공간의 프로그램 카운터(PC)와 스택 정보를 찍어주네요.
 
<7>[   72.248793 / 05-21 22:27:31.925][2] android.ui: unhandled page fault (11) at 0x00000000, code 0x005
<1>[   72.248810 / 05-21 22:27:31.925][2] pgd = da0fc000
<1>[   72.252580 / 05-21 22:27:31.925][2] [00000000] *pgd=00000000
<6>[   72.258764 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258781 / 05-21 22:27:31.935][2] task: d7e0d840 ti: d7f02000 task.ti: d7f02000
<6>[   72.258793 / 05-21 22:27:31.935][2] PC is at 0x73a7c9c0
<6>[   72.258803 / 05-21 22:27:31.935][2] LR is at 0x0
<6>[   72.258814 / 05-21 22:27:31.935][2] pc : [<73a7c9c0>]    lr : [<00000000>]    psr: 00070030
<6>[   72.258814 / 05-21 22:27:31.935][2] sp : 885bc480  ip : 00000000  fp : 131e3218
<6>[   72.258830 / 05-21 22:27:31.935][2] r10: 71488688  r9 : 92778200  r8 : 00000000
<6>[   72.258840 / 05-21 22:27:31.935][2] r7 : 131e2bf0  r6 : 00000000  r5 : 00000001  r4 : 00263c64
<6>[   72.258851 / 05-21 22:27:31.935][2] r3 : 131e3218  r2 : 71488688  r1 : 00000000  r0 : 71488688
<6>[   72.258863 / 05-21 22:27:31.935][2] Flags: nzcv  IRQs on  FIQs on  Mode USER_32  ISA Thumb  Segment user
<6>[   72.258873 / 05-21 22:27:31.935][2] Control: 10c0383d  Table: 9a0fc06a  DAC: 00000051
<6>[   72.258885 / 05-21 22:27:31.935][2] CPU: 2 PID: 999 Comm: android.ui Tainted: G        W      3.18.66-gbbd0bc4-dirty #3
<6>[   72.258907 / 05-21 22:27:31.935][2] [<c010e400>] (unwind_backtrace) from [<c010b4a8>] (show_stack+0x10/0x14)
<6>[   72.258924 / 05-21 22:27:31.935][2] [<c010b4a8>] (show_stack) from [<c0fd0740>] (dump_stack+0x78/0x98)
<6>[   72.258940 / 05-21 22:27:31.935][2] [<c0fd0740>] (dump_stack) from [<c0115f8c>] (__do_user_fault+0x108/0x19c)
<6>[   72.258957 / 05-21 22:27:31.935][2] [<c0115f8c>] (__do_user_fault) from [<c0fe05b8>] (do_page_fault+0x33c/0x3e8)
<6>[   72.258972 / 05-21 22:27:31.935][2] [<c0fe05b8>] (do_page_fault) from [<c0100404>] (do_DataAbort+0x34/0x184)
<6>[   72.258986 / 05-21 22:27:31.935][2] [<c0100404>] (do_DataAbort) from [<c0fdec3c>] (__dabt_usr+0x3c/0x40)
<6>[   72.258997 / 05-21 22:27:31.935][2] Exception stack(0xd7f03fb0 to 0xd7f03ff8)
<6>[   72.259009 / 05-21 22:27:31.935][2] 3fa0:                                     71488688 00000000 71488688 131e3218
<6>[   72.259023 / 05-21 22:27:31.935][2] 3fc0: 00263c64 00000001 00000000 131e2bf0 00000000 92778200 71488688 131e3218
<6>[   72.259036 / 05-21 22:27:31.935][2] 3fe0: 00000000 885bc480 00000000 73a7c9c0 00070030 ffffffff
 
이럴 때 어느 함수에 T32 브레이크 포인트를 걸어야 할까요?
__do_user_fault() 함수의 CONFIG_DEBUG_USER 안에 걸며 딱이겠죠.
 
static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
unsigned int fsr, unsigned int sig, int code,
struct pt_regs *regs)
{
struct siginfo si;
 
trace_user_fault(tsk, addr, fsr);
 
#ifdef CONFIG_DEBUG_USER
if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||  //<<-- 여기 브레이크 포인트
    ((user_debug & UDBG_BUS)  && (sig == SIGBUS))) {
printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
       tsk->comm, sig, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
}
#endif
 
__do_user_fault() 함수를 호출하는 루틴
static int __kprobes
do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
// ... 생략 ...
 
if (user_mode(regs))
flags |= FAULT_FLAG_USER;
if (fsr & FSR_WRITE)
flags |= FAULT_FLAG_WRITE;
 
// .. 생략 ...
 
__do_user_fault(tsk, addr, fsr, sig, code, regs);
return 0;
 
 no_context:
__do_kernel_fault(mm, addr, fsr, regs);
return 0;
}
 
가끔 T32 잘 쓴다고 자랑해보세요.
 
# Reference: For more information on 'Linux Kernel';
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
 
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
 
 
 

+ Recent posts