← ARMv9 해설DUJINLABS.COM

Source deep dive 01 · Exception vector · SVC · pt_regs

ARM64 Exception과 system call 소스 분석

응용 프로그램의 write() 한 번이 CPU의 Exception Level을 바꾸고 Linux 함수로 들어갔다가 다시 EL0로 돌아오는 전 과정을, 처음에는 문 하나를 통과하는 비유로 설명하고 마지막에는 실제 entry assembly와 C 호출 경로로 추적한다.

Kernel
Linux v6.18.37
Entry
EL0 → EL1
Path
SVC · abort
Depth
입문 → 소스

Exception은 오류만 뜻하지 않는다

일반 프로그램은 EL0에서 실행되며 kernel memory나 장치를 직접 만질 수 없다. 파일 쓰기처럼 kernel 권한이 필요한 일을 요청하면 SVC 명령으로 EL1의 정해진 입구를 호출한다. page fault, interrupt, breakpoint도 같은 큰 틀의 exception이지만 발생 원인과 돌아갈 위치가 다르다.

System call

프로그램이 의도적으로 kernel 서비스를 요청한다.

Fault

현재 명령을 완료하려면 page table 수정이나 signal 처리가 필요하다.

IRQ

장치가 CPU의 현재 명령과 독립적으로 처리를 요청한다.

Debug

breakpoint·single-step 같은 디버그 사건이다.

CPU가 EL1 진입 순간 보존하는 최소 상태

상태역할Linux에서의 사용
ELR_EL1돌아갈 instruction 주소pt_regs.pc로 옮겨 signal·ptrace·복귀에 사용한다.
SPSR_EL1exception 전 PSTATE원래 EL, interrupt mask, condition flag를 복원한다.
ESR_EL1Exception Class와 syndromeSVC, instruction abort, data abort, debug 중 어느 handler로 갈지 고른다.
FAR_EL1fault 관련 virtual addresspage fault라면 VMA와 page table을 찾는 주소가 된다.
VBAR_EL1vector table 시작 주소현재 EL·stack·AArch64 여부에 따른 entry slot을 선택한다.

중요: CPU가 모든 general-purpose register를 자동 저장하는 것은 아니다. Linux의 kernel_entry assembly가 x0~x30과 관련 상태를 stack의 struct pt_regs 형태로 만든다.

Vector slot에서 C handler까지

개념 호출 흐름

EL0 executes SVC #0
→ VBAR_EL1의 EL0t synchronous slot
→ entry.S: kernel_entry 0
→ entry-common.c: el0t_64_sync_handler(regs)
→ ESR_EL1.EC == SVC64
→ el0_svc(regs)

entry.S는 register 저장과 stack·보안 상태 전환을 담당하고, entry-common.c는 ESR의 Exception Class를 읽어 C handler를 선택한다. 두 파일의 책임을 섞어 읽으면 호출 경로를 놓치기 쉽다.

kernel_entryel0t_64_sync_handler()가 첫 번째 핵심 지점이다.

write()가 실제 system call 함수에 도달하는 길

  1. userspace wrapper가 x0~x5에 인수, x8에 system call 번호를 넣고 svc #0을 실행한다.
  2. el0t_64_sync_handler()가 ESR의 EC를 판별해 el0_svc()를 호출한다.
  3. el0_svc()do_el0_svc()로 넘긴다.
  4. do_el0_svc()는 x8 값을 el0_svc_common()에 전달한다.
  5. invoke_syscall()이 번호 범위를 검사하고 sys_call_table[scno]를 선택한다.
  6. 반환값은 x0 위치인 regs->regs[0]에 기록된다.
  7. 진입 시 보존한 상태를 검사하고 kernel_exit 0ERET으로 EL0에 돌아간다.

Linux v6.18.37의 실제 연결점은 do_el0_svc(), el0_svc_common(), invoke_syscall()이다.

인수와 반환값은 pt_regs를 통해 보인다

AArch64 ABISystem call 의미저장 위치
x0~x5최대 6개 인수regs->regs[0..5]
x8system call 번호regs->regs[8]
x0성공 값 또는 음수 errno복귀 전 regs->regs[0]
PC/PSTATE복귀 주소와 상태regs->pc, regs->pstate

strace는 이 ABI 경계를 보여 주지만 kernel 내부 함수 호출을 보여 주지는 않는다. 내부 흐름은 tracepoint, ftrace 또는 BPF를 추가해야 한다.

Data abort는 system call과 다른 분기다

EL0 load/store가 translation·permission·tag 검사에 실패하면 ESR의 Data Abort EC와 ISS, FAR이 채워진다. el0t_64_sync_handler()는 이를 do_mem_abort()로 보내고, fault status table이 translation fault·permission fault·MTE tag fault 등에 맞는 handler를 고른다.

Page fault 경로

el0t_64_sync_handler
→ do_mem_abort(FAR_EL1, ESR_EL1, regs)
→ fault_info[DFSC].fn
→ do_page_fault
→ lock_vma_under_rcu / find_and_lock_anon_vma
→ handle_mm_fault
→ page table 생성 또는 SIGSEGV

do_mem_abort()do_page_fault()를 이어서 읽는다.

실제 entry code가 추가로 처리하는 것

register 저장만 하는 것이 아니다. context tracking, lockdep, RCU, tracing, MTE의 TCO 상태, pointer authentication key, single-step, signal과 rescheduling 여부가 진입·복귀 경계에서 정리된다. 그래서 entry 경로에는 noinstr가 많이 붙고 일반 C 함수처럼 임의 instrumentation을 넣으면 안 된다.

분석 원칙: entry assembly에 printk를 넣는 방식으로 실험하지 않는다. tracepoint·function graph·kprobe가 허용되는 지점부터 관찰한다.

Linux v6.18.37 소스 지도

파일핵심 심볼확인 질문
arch/arm64/kernel/entry.Skernel_entry, kernel_exit무엇을 stack에 저장하고 언제 ERET하는가.
entry-common.cel0t_64_sync_handler, el1h_64_sync_handlerESR EC가 어느 C handler를 선택하는가.
syscall.cdo_el0_svc, invoke_syscallx8 번호가 syscall table로 어떻게 연결되는가.
arch/arm64/mm/fault.cdo_mem_abort, do_page_faultDFSC와 FAR이 page fault나 signal로 어떻게 바뀌는가.
arch/arm64/include/asm/ptrace.hstruct pt_regs저장된 register layout이 무엇인가.

장비에서 호출 경로를 재현한다

System call 관찰

strace -e write ./demo
sudo trace-cmd record -e raw_syscalls ./demo
sudo trace-cmd report
sudo perf trace -e write ./demo

그다음 ftrace의 function graph filter에 do_el0_svc 대신 instrumentation 가능한 syscall 하위 함수를 좁혀 넣는다. entry의 noinstr 함수가 보이지 않는 것은 도구 실패가 아니라 의도된 제약일 수 있다.

fault 실험은 보호된 test process에서 unmapped address나 read-only page write를 만들고 perf trace, signal handler의 siginfo_t.si_code, kernel trace를 함께 저장한다.

자주 틀리는 해석

  • SVC가 곧 함수 호출인 것은 아니다. Exception Level과 stack·PSTATE를 바꾸는 architecture event다.
  • ESR은 원인의 모든 정보를 주지 않는다. FAR validity, instruction syndrome와 page table 상태를 함께 봐야 한다.
  • page fault는 항상 오류가 아니다. demand paging과 copy-on-write의 정상 경로이기도 하다.
  • strace 출력만으로 kernel 내부 병목을 확정할 수 없다.

근거 자료