Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[Linux][Kernel] current 매크로 (2) [라즈베리파이][커널]매크로분석

그럼 current란 코드의 정체를 조금 더 알아볼까요?
이번에도 패치 코드 하나를 소개할게요. 다음 루틴은 Wait Queue 관련 함수들인데요.
diff --git a/kernel/sched/wait.c b/kernel/sched/wait.c
index 9453efe..a1371a9 100644
--- a/kernel/sched/wait.c
2+++ b/kernel/sched/wait.c
@@ -76,6 +76,16 @@ static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
        }
 }

1 +void trace_kernel_process_name(void *param)
2 +{
3 +       char *proc_name;
4 +       struct task_struct *proc_ptr = (struct task_struct *)param;
5 +
6 +       proc_name = proc_ptr->comm;
7 +
8 +       printk("process name: %s \n", proc_name);
9 +}
10 +
 /**
  * __wake_up - wake up threads blocked on a waitqueue.
  * @q: the waitqueue
@@ -91,6 +101,7 @@ void __wake_up(wait_queue_head_t *q, unsigned int mode,
11 {
12       unsigned long flags;
13
14 +       trace_kernel_process_name(current);
15        spin_lock_irqsave(&q->lock, flags);
16       __wake_up_common(q, mode, nr_exclusive, 0, key);
17        spin_unlock_irqrestore(&q->lock, flags);

이제 차근차근 패치 코드를 살펴볼까요?
14번째 줄 코드는 current란 변수를 파라미터로 trace_kernel_process_name 함수에 전달합니다.
14 +       trace_kernel_process_name(current);
 
다음, 첫 번째부터 네 번째 코드를 살펴볼까요? 
첫 번째 코드를 보면 void 포인터 타입의 param 변수를 파라미터로 전달합니다. 
네 번째 코드에서는 param이란 파라미터를 (struct task_struct *) 형태로 캐스팅해서 proc_ptr 지역변수에 복사합니다. 

여기서 param이란 파라미터는 __wake_up 함수에서 current 변수로 전달하고 있다는 것을 눈여겨볼 필요가 있습니다. 즉 param은 현재 구동 중인 프로세스의 태스크 디스크립터 정보를 담고 있습니다.

이제 나머지 코드를 볼게요. struct task_struct구조체 멤버 중 char comm[TASK_COMM_LEN] 이 있습니다. 프로세스 이름을 char 배열 형태로 담고 있습니다. 6번째 줄 코드는 이 값을 char 타입의 proc_name 변수에 저장하고, 8번째 줄 코드에서 printk란 함수로 프로세스 이름을 커널 로그로 출력합니다.
6 +       proc_name = proc_ptr->comm;
7 +
8 +       printk("process name: %s \n", proc_name);
 
이번에도 위와 같이 코드를 수정하고 컴파일을 하면 전처리 파일이 생성되겠죠? 그럼 current란 변수가 실제 어떤 형태인지 전처리 파일을 열어서 볼까요? [kernel/sched/wait.c] 파일에 대한 전처리 파일은 [kernel/sched/.tmp_wait.i] 이란 점 참고하세요.

패치 코드에서 current 변수는 trace_kernel_process_name 이란 함수 호출 시 파라미터로 전달됐습니다. 그럼 __wake_up 함수에서 trace_kernel_process_name 함수를 호출한 코드를 봐야겠죠.
1 void __wake_up(wait_queue_head_t *q, unsigned int mode,
2    int nr_exclusive, void *key)
3 {
4  unsigned long flags;
5
6  trace_kernel_process_name((current_thread_info()->task));
7  do { do { ({ unsigned long __dummy; typeof(flags) __dummy2; (void)(&__dummy == &__dummy2); 1; }); flags = _raw_spin_lock_irqsave(spinlock_check(&q->lock)); } while (0); } while (0);
8  __wake_up_common(q, mode, nr_exclusive, 0, key);
 
6번째 줄 코드를 보니 이상한 코드가 보입니다. 분명히 current 변수로 trace_kernel_process_name 함수 파라미터를 전달했는데, (current_thread_info()->task) 코드로 변경됐습니다.

무슨 일이 일어난 건지 조금 더 짚어볼까요?
[kernel/sched/.tmp_wait.i] 전처리 파일에서 current_thread_info 코드를 검색해볼까요? 이 코드가 매크로로 선언됐을 수도 있거든요. 검색하니 다음과 같이 코드가 보입니다.
1 register unsigned long current_stack_pointer asm ("sp");
2
3 static inline __attribute__((always_inline)) __attribute__((no_instrument_function)) struct thread_info *current_thread_info(void)
4 {
5  return (struct thread_info *)
6   (current_stack_pointer & ~((((1UL) << 12) << 1) - 1));
7 }

그럼 위 코드가 어떻게 실행되는지 차례대로 살펴볼게요.
1. 첫 번째 줄 코드가 실행되면 현재 실행 중인 프로세스의 스택 주소를 읽어옵니다. 이 스택 주소를 current_stack_pointer 변수에 저장합니다. 

2.(current_stack_pointer & ~0x1fff) 비트 연산으로 현재 프로세스의 스택 Top 주소를 계산합니다. 스택 Top 주소에 (struct thread_info*)란 구조체 멤버들이 있거든요.

3.(struct thread_info*) 구조체에 task 멤버를 리턴 합니다. 
current_thread_info()->task

task란 멤버는 (struct task_struct *) 구조체인데 이는 해당 프로세스의 태스크 디스크립터입니다. 
6 +       proc_name = proc_ptr->comm;
7 +
8 +       printk("process name: %s \n", proc_name);






핑백

덧글

댓글 입력 영역