Linux Kernel(4.14) Hacks

rousalome.egloos.com

포토로그 Kernel Crash




[라즈베리파이][리눅스커널] 인터럽트 벡터: 스택 푸시 [라즈베리파이][커널]인터럽트

인터럽트가 발생했을 때 구동 중인 프로세스의 레지스터가 스택 메모리 공간에 푸쉬된다고 설명했습니다. 이번에 실제 Trace32 프로그램으로 스택 메모리 덤프를 확인해 보겠습니다. 

아래 콜스택은 ret_fast_syscall 함수에서 시작해서 do_fsync 함수 방향으로 호출하여 account_group_exec_runtime까지 실행하고 있는 흐름입니다.
-000|account_group_exec_runtime(inline)
-000|update_curr()
-001|check_spread(inline)
-001|put_prev_entity()
-002|put_prev_task_fair()
-003|pick_next_task_rt(inline)
-003|pick_next_task_rt()
-004|pick_next_task(inline)
-004|__schedule()
-005|arch_local_irq_disable(inline)
-005|preempt_schedule_irq()
-006|svc_preempt(asm)
-007|__irq_svc(asm)
 -->|exception
-008|blk_flush_plug_list()
-009|current_thread_info(inline)
-009|blk_finish_plug()
-010|ext4_writepages()
-011|__filemap_fdatawrite_range()
-012|filemap_write_and_wait_range()
-013|ext4_sync_file()
-014|vfs_fsync()
-015|fdput(inline)
-015|do_fsync()
-016|ret_fast_syscall(asm)

여기서 7번 콜스택에 __irq_svc 란 인터럽트 벡터 함수가 보이죠? 이를 어떻게 해석할까요? 인터럽트가 발생했다고 봐야겠죠.

조금 더 자세히 살펴보면 ret_fast_syscall -- blk_flush_plug_list 사이 콜스택 흐름으로 동작하고 있었는데 blk_flush_plug_list 함수 실행 도중 인터럽트가 발생해서 __irq_svc란 인터럽트 벡터가 실행된 거죠. 이후 인터럽트 처리를 위한 서브 루틴이 실행 중이었습니다. 이제 어떤 로그에서도 __irq_svc를 보면 편하게 분석할 수 있겠죠?

그럼 위 콜스택에서 인터럽트 벡터인 __irq_svc 함수가 호출된 시점의 스택 메모리 주소 0xCE4F9D84로 이동해서 스택 덤프를 확인하면 아래 화살표와 같이 스택에 푸쉬된 레지스터를 확인할 수 있습니다. 
[명령어: d.v %symbol.l 0xCE4F9D80]
________address||value_______|symbol
NSD:CE4F9D80| 0x20070013
NSD:CE4F9D84| 0xFFFFFFFF   
NSD:CE4F9D88| 0xCE4F9DE4
NSD:CE4F9D8C| 0xC0FF97B4   \vmlinuxGlobal__irq_svc+0x74
NSD:CE4F9D90| 0xCE4F8000
NSD:CE4F9D94| 0xCE4F8000
NSD:CE4F9D98| 0xCE4F9DAC
NSD:CE4F9D9C| 0xC0FF4E2C   \vmlinuxsched/corepreempt_schedule_irq+0x50
NSD:CE4F9DA0| 0xC039B9DC   \vmlinuxblk-coreblk_flush_plug_list+0x1A4
NSD:CE4F9DA4| 0xC039B9E0   \vmlinuxblk-coreblk_flush_plug_list+0x1A8
NSD:CE4F9DA8| 0x1           
NSD:CE4F9DAC| 0xC0FF97D8   \vmlinuxGlobalsvc_preempt+0x8
NSD:CE4F9DB0| 0x0           // <<--[1] R0   
NSD:CE4F9DB4| 0xCECC72B4   // <<--[2] R1   
NSD:CE4F9DB8| 0x1           // <<--[3] R2         
NSD:CE4F9DBC| 0x0           // <<--[4] R3
NSD:CE4F9DC0| 0xCE4F9E00   // <<--[5] R4
NSD:CE4F9DC4| 0xEAD60A60   // <<--[6] R5
NSD:CE4F9DC8| 0x1           // <<--[7] R6
NSD:CE4F9DCC| 0xCE4F9E00   //<<--[8] R7 
NSD:CE4F9DD0| 0x0            //<<--[9] R8
NSD:CE4F9DD4| 0x60070013   // <<--[10] R9
NSD:CE4F9DD8| 0xCE4F8000   // <<--[11] R10
NSD:CE4F9DDC| 0x1           // <<--[12] R11
NSD:CE4F9DE0| 0x60070093   // <<--[13] R12
NSD:CE4F9DE4| 0xCE4F9E00  // <<--[14] R13, 스택 주소
NSD:CE4F9DE8| 0xC039B9DC \vmlinuxblk-coreblk_flush_plug_list+0x1A4//[15]R14
NSD:CE4F9DEC| 0xC039B9E0   \vmlinuxblk-coreblk_flush_plug_list+0x1A8//[16]PC
NSD:CE4F9DF0| 0x20070013
NSD:CE4F9DF4| 0xFFFFFFFF

위 메모리 덤프를 보면 0x4c 공간만큼 스택 공간을 할당한 다음에 실행 중인 레지스터를 r0부터 pc까지 스택에 저장하고 있습니다. 이 레지스터들은 인터럽트를 처리하고 난 다음에 다시 이전에 실행 중인 프로세스가 다시 읽습니다. 이후 어떤 동작을 할까요? 당연히 인터럽트가 발생해서 멈췄던 코드 다음부터 실행하는 겁니다. 

인터럽트 벡터를 어셈블리로 분석하는 것은 매우 어려운 도전입니다. 잘 이해가 안 갈 수도 있어요. 저도 처음에 잘 머리에 들어오지 않았거든요. 하지만 몇 번 메모리에 레지스터가 쌓이는 동작을 그리면 프로세스 정보를 저장하는 동작이 더 오랫동안 남을 겁니다.

이제 인터럽트 벡터에서 프로세스 레지스터를 저장하고 서브 루틴을 실행하는 코드까지 확인했으니 다음 장에선 인터럽트 핸들러를 처리하는 흐름을 배워 볼게요.


핑백

덧글

댓글 입력 영역