[RISC-V] tp 레지스터 (Register)의 핵심 정보 - task_struct
Background
리눅스 커널은 함수와 자료 구조로 구성되어 있다. 자료 구조 중에서 가장 중요한 것은 무엇일까? 정답은 없지만, 많은 커널 개발자들은 task_struct 구조체라고 답할 것이다.
task_struct는 프로세스의 속성 정보를 나타내는 구조체이다. task_struct 구조체의 주소만 알면 커널의 대부분의 정보를 확인할 수 있다. 예를 들면:
-모든 프로세스의 task_struct 구조체 주소
-모든 프로세스의 콜 스택
-모든 프로세스별로 오픈한 파일 디스크립터 정보
그런데 이러한 task_struct 구조체가 범용 레지스터에서 확인될 수 있다면 어떤 생각이 들까? 아마 다음과 같을 것이다:
디버깅 관점: 디버깅을 매우 효율적으로 할 수 있겠다.
보안 관점: 시스템의 중요한 정보를 레지스터에서 확인할 수 있으므로 주의해야 한다.
RISC-V 아키텍처에서 커널의 tp 레지스터는 해당 프로세스의 task_struct 구조체의 주소를 저장한다. 이 내용을 보고 “정말 그럴까?”라는 생각이 들 수도 있다.
Debugging - task_struct
이제부터 RISC-V 디바이스에서 추출한 메모리 덤프를 분석해보자.
아래는 익셉션으로 인해 커널 크래시가 발생했을 때 출력된 커널 로그의 일부이다.
CPU1에서, PID가 1132인 bash 프로세스가 어떤 코드를 실행하다가 익셉션이 발생한 것을 확인할 수 있다.
우리가 주목해야 할 부분은 tp 레지스터가 ffffffd8c21be780 값을 포함하고 있다는 사실이다.
그렇다면 이번에는 Crash Utility에서 runq -m 명령어를 실행하여 각 코어에서 실행 중인 프로세스의 정보를 확인해 보자.
TASK: ffffffd8c21be780이라는 시그니처는 프로세스의 task_struct 주소를 나타낸다.
이 ffffffd8c21be780 주소가 정말 task_struct의 주소인지 확인하기 위해 다음과 같이 검증해 보자.
메모리를 확인해 보면 ffffffd8c21be780 주소가 실제로 task_struct 구조체의 시작 주소임을 알 수 있다.
Debugging - task_struct.thread
이번에는 또 다른 디버깅을 진행해 보자. 리눅스 커널에서 task_struct.thread 필드는 CPU 아키텍처에 의존적인 프로세스 정보를 저장한다.
이를 종합하면, task_struct.thread 필드의 타입은 struct thread_struct라는 것을 알 수 있다.
이제 task_struct.thread 정보를 Crash Utility로 확인해 보자.
task_struct.thread.s[3] 필드에서 ffffffd8c21be780 주소가 확인된다.
Summary
디버깅을 통해 다음과 같은 사실을 확인할 수 있다:
-tp 레지스터에는 task_struct의 주소가 저장되어 있다.
-task_struct.thread.s[3] 필드에도 task_struct의 주소가 저장되어 있다.
이 정보를 활용하면 더욱 효율적인 디버깅이 가능하다. 잘 참고하자.