리눅스 커널에서 sys_enter ftrace 이벤트를 키면 syscall_trace_enter() 함수가 호출된다.
syscall_trace_enter() 함수의 첫 번째 인자로 'struct pt_regs *regs' 가 전달되는데 이 정체를 확인해보자.
다음은 syscall_trace_enter() 함수의 구현부이다.
asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno)
{
current_thread_info()->syscall = scno;
if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER);
syscall_trace_enter() 함수는 어디서 호출될까?
다음 코드와 같이 __sys_trace 레이블에서 syscall_trace_enter() 함수를 호출한다.
arch\arm\kernel\entry-common.S
01 80101154 <__sys_trace>:
02 80101154: e1a01007 mov r1, r7
03 80101158: e28d0008 add r0, sp, #8
04 8010115c: eb002728 bl 8010ae04 <syscall_trace_enter>
위 코드에서 03번째 줄을 유심히 보자. 스택 포인터에 8을 더해 r0 레지스터에 저장한다.
__sys_trace 레이블은 누가 호출할까?
vector_swi 레이블의 첫 번째 코드를 보자.
NSR:80107EE0|vector_swi: sub r13,r13,#0x48 ; r13,r13,#72
NSR:80107EE4| stm r13,{r0-r12}
NSR:80107EE8| add r8,r13,#0x3C ; r8,r13,#60
NSR:80107EEC| stmdb r8,{r13-r14}^
NSR:80107EF0| mrs r8,spsr
NSR:80107EF4| str r14,[r13,#0x3C]
NSR:80107EF8| str r8,[r13,#0x40]
NSR:80107EFC| str r0,[r13,#0x44]
NSR:80107F00| mov r11,#0x0 ; r11,#0
NSR:80107F04| mrc p15,0x0,r12,c1,c0,0x0 ; p15,0,r12,c1,c0,0 (system control)
NSR:80107F08| ldr r10,0x80107FC0 ; r10,__cr_alignment
NSR:80107F0C| ldr r10,[r10]
NSR:80107F10| teq r10,r12
NSR:80107F14| mcrne p15,0x0,r10,c1,c0,0x0 ; p15,0,r10,c1,c0,0 (system control)
NSR:80107F18| push {r0-r3,r12,r14}
NSR:80107F1C| bl 0x801DD5C8 ; trace_hardirqs_on
NSR:80107F20| pop {r0-r3,r12,r14}
NSR:80107F24| cpsie i
NSR:80107F28| lsr r9,r13,#0x0D
NSR:80107F2C| lsl r9,r9,#0x0D
NSR:80107F30| adr r8,0x80107FC4 ; r8,sys_call_table
NSR:80107F34|local_restart: ldr r10,[r9]
NSR:80107F38| push {r4-r5}
NSR:80107F3C| tst r10,#0xF0 ; r10,#240
NSR:80107F40| bne 0x80107F68 ; __sys_trace
스택에서 0x48 만큼 뺀다.
다음은 유저 프로세서의 커널 공간의 스택 최하단 주소의 메모리 덤프이다.
________address|_data________|value_____________|symbol
NSD:DA81BFA8| C0 E8 17 A2 0xA217E8C0
NSD:DA81BFAC| C8 E9 17 A2 0xA217E9C8
NSD:DA81BFB0| 90 E8 17 A2 0xA217E890 //<<--[1]
NSD:DA81BFB4| 00 00 00 00 0x0
NSD:DA81BFB8| 00 00 00 00 0x0
NSD:DA81BFBC| 08 00 00 00 0x8
NSD:DA81BFC0| C0 E8 17 A2 0xA217E8C0
NSD:DA81BFC4| C8 E9 17 A2 0xA217E9C8
NSD:DA81BFC8| 90 E8 17 A2 0xA217E890
NSD:DA81BFCC| B1 00 00 00 0xB1
NSD:DA81BFD0| 20 D5 0C A4 0xA40CD520
NSD:DA81BFD4| 59 00 00 00 0x59
NSD:DA81BFD8| 00 CC 8F AB 0xAB8FCC00
NSD:DA81BFDC| 58 AF 79 AB 0xAB79AF58
NSD:DA81BFE0| 30 D5 0C A4 0xA40CD530
NSD:DA81BFE4| 90 E8 17 A2 0xA217E890
NSD:DA81BFE8| F3 DB CE AE 0xAECEDBF3
NSD:DA81BFEC| 44 7A D1 AE 0xAED17A44
NSD:DA81BFF0| 10 00 0F 20 0x200F0010
NSD:DA81BFF4| 90 E8 17 A2 0xA217E890
NSD:DA81BFF8| 00 00 00 00 0x0
NSD:DA81BFFC| FC 02 00 00 0x2FC
NSD:DA81C000| 80 00 00 00 0x80 //<- 스택 최하단 주소
[1] 스택 최하단 주소인 DA81C000이니 디폴트 스택 주소는 DA81BFF0이다. 여기서 0x48를 빼면 DA81BFA8가 된다.
바로 vector_swi 레이블의 첫 줄의 코드가 실행하면 업데이트되는 코드이다.
NSR:80107EE0|vector_swi: sub r13,r13,#0x48 ; r13,r13,#72
NSR:80107EE4| stm r13,{r0-r12}
그런데 __sys_trace 레이블에서 스택 주소에서 +0x8만큼 더한다.
arch\arm\kernel\entry-common.S
01 80101154 <__sys_trace>:
02 80101154: e1a01007 mov r1, r7
03 80101158: e28d0008 add r0, sp, #8
위 코드가 실행되면 결국 스택 주소는 DA81BFB0(DA81BFA8 + 0x8)이 된다.
프로세스의 메모리 덤프에서 유저 공간에서 실행된 레지스터 세트의 정보와 스택 주소는 다음과 같다.
________address|_data________|value_____________|symbol
NSD:DA81BFA8| C0 E8 17 A2 0xA217E8C0 //<<--[1]
NSD:DA81BFAC| C8 E9 17 A2 0xA217E9C8
NSD:DA81BFB0| 90 E8 17 A2 0xA217E890 //<<--[2] __sys_trace 레이블이 실행된 후 업데이트 되는 스택 주소, struct pt_regs
NSD:DA81BFB4| 00 00 00 00 0x0 // r1
NSD:DA81BFB8| 00 00 00 00 0x0 // r2
NSD:DA81BFBC| 08 00 00 00 0x8 // r3
NSD:DA81BFC0| C0 E8 17 A2 0xA217E8C0 // r4
NSD:DA81BFC4| C8 E9 17 A2 0xA217E9C8 // r5
NSD:DA81BFC8| 90 E8 17 A2 0xA217E890 // r6
NSD:DA81BFCC| B1 00 00 00 0xB1 // r7: 시스템 콜 번호
NSD:DA81BFD0| 20 D5 0C A4 0xA40CD520
NSD:DA81BFD4| 59 00 00 00 0x59
NSD:DA81BFD8| 00 CC 8F AB 0xAB8FCC00
NSD:DA81BFDC| 58 AF 79 AB 0xAB79AF58
NSD:DA81BFE0| 30 D5 0C A4 0xA40CD530
NSD:DA81BFE4| 90 E8 17 A2 0xA217E890
NSD:DA81BFE8| F3 DB CE AE 0xAECEDBF3
NSD:DA81BFEC| 44 7A D1 AE 0xAED17A44
NSD:DA81BFF0| 10 00 0F 20 0x200F0010
NSD:DA81BFF4| 90 E8 17 A2 0xA217E890
NSD:DA81BFF8| 00 00 00 00 0x0
NSD:DA81BFFC| FC 02 00 00 0x2FC
NSD:DA81C000| 80 00 00 00 0x80 //<- 스택 최하단 주소
커널에서 어떤 함수를 사용하면 이 정보를 파악할 수 있을까?
#define task_pt_regs(p) \
((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
task_pt_regs() 함수를 호출하면 되지 않을까?
'Core BSP 분석 > 리눅스 커널 핵심 분석' 카테고리의 다른 글
[리눅스커널] CPU 코어의 주파수(Frequency) 확인하기 - cpufreq_cpu_data (0) | 2023.05.06 |
---|---|
[리눅스커널] 디버깅: 커널 로그 레벨(/proc/sys/kernel/printk)을 누가 설정하나? (0) | 2023.05.06 |
[리눅스커널] ARMv8: 슬럽 오브젝트의 트랙(track) 구조체를 TRACE32로 디버깅하기 (0) | 2023.05.06 |
[리눅스커널] ftrace: 콜 스택을 메시지로 출력하기(CALLER_ADDR0~CALLER_ADDR3) (0) | 2023.05.06 |
[리눅스커널] 특정 CPU를 Isolation 시키고 싶은 경우 (0) | 2023.05.06 |