본문 바로가기

시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리

ARM64- 스택 푸쉬(Stack Push) Userspace -> Kernel Space

유저 스페이스에서 커널 스페이스로 전환하려면 시스템 콜을 호출해야 한다 것은 마르고 닳도록 들었죠?
이번에는 ARM64 Architecture에서 EL0 ->EL1로 변환될 시, Stack을 어떻게 Push하는 지 살펴볼께요.
 
"rild"란 프로세스의 콜스택을 예를 들어 볼 께요. 잘 보면, 유저 공간에서 sendto란 시스템 콜을 호출했다는 걸 알 수 있네요.
-000|do_mem_abort()
-001|el1_da(asm)
 -->|exception
-002|ch_pop_remote_rx_intent()
-003|glink_tx_common()
-004|glink_txv()
-005|ipc_router_glink_xprt_write()
-006|msm_ipc_router_write_pkt(inline)
-006|msm_ipc_router_send_to()
-007|msm_ipc_router_sendmsg()
-008|sock_sendmsg_nosec(inline)
-008|sock_sendmsg()
-009|SYSC_sendto(inline)
-009|sys_sendto()
-010|el0_svc_naked(asm)
.
아래는 "rild"란 프로세스의 task descriptor(주소: 0xFFFFFFE577E1A400) 인데요. stack란 멤버가 0xFFFFFFE4DE6A4000을 가르키고 있는데요, 이 주소는 스택의 Top Address를 가르켜요.
  (struct task_struct*)0xFFFFFFE577E1A400 = 0xFFFFFFE577E1A400 -> (
    state = 0x0,
    stack = 0xFFFFFFE4DE6A4000,
    usage = (counter = 0x2),
// .. 생략..
    comm = "rild",
    nameidata = 0x0,
.
리눅스 커널에서 스택은 Bottom Address(높은 주소) 에서 Top Address(낮은 주소로) 자란다는 사실은 다 아실 꺼에요. ARM64(Aarch64) 아키텍처에서는 커널 공간의 프로세스 스택 주소는 0x4000바이트로 이거든요. 그럼 rild"란 프로세스는 (0xFFFFFFE4DE6A4000+0x4000) 에서 0xFFFFFFE4DE6A4000 주소로 스택이 자라난다는 사실을 알 수 있어요. 스택이 자라난다는 소리가 뭐냐구요? 스택 공간에는 로컬 변수를 할당 받아 쓰고 가장 중요한 함수가 호출될 때 돌아갈 주소(Linked Register)와 돌아갈 주소가 있는 주소(Frame Pointer 주소)를 스택에 저장하거든요. 스택에 어떤 값을 푸쉬하면 자연히 스택 주소가 점점 작아져요. 이걸 스택이 자란다고 할 수 있죠. 높은 주소에서 낮은 주소로요. 
 
만약 A -> B -> C 순서로 함수가 호출되었으면 아래와 같은 심볼 정보 확인을 할 수 있어요. 아래는 정말 쉽게 설명한 도식인데. 나중에 ARM64 아키텍쳐의 Calling Convention은 자세히 다룰 예정이에요.
0xFFFFFFE4DE6A4000   //<<-- 낮은 주소(stack top 주소)
// 생략
0xFFFFFFE4DE6A7FA0       D
0xFFFFFFE4DE6A7FA8 
0xFFFFFFE4DE6A7FB0
0xFFFFFFE4DE6A7FB8       C 
0xFFFFFFE4DE6A7FC0
0xFFFFFFE4DE6A7FC8
0xFFFFFFE4DE6A7FD0       B
0xFFFFFFE4DE6A7FD8 
0xFFFFFFE4DE6A7FE0
0xFFFFFFE4DE6A7FE8
0xFFFFFFE4DE6A7FF0       A
0xFFFFFFE4DE6A7FF8
0xFFFFFFE4DE6A8000     //<<-- 높은 주소(stack bottom 주소)
.
그럼 스택 bottom 주소 0xFFFFFFE4DE6A8000로 가볼까요? 잘 보면 유저 공간에서 구동 중인 레지스터가 스택에 Push된 걸 알 수 있어요.
_____________address|_data____________________|value_____________|symbol
NUD:FFFFFFE4DE6A7E60| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E68| 30 31 68 99 9F FF FF FF  0xFFFFFF9F99683130 \\vmlinux\Global\el0_svc_naked+0x24
NUD:FFFFFFE4DE6A7E70| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E78| 28 DF FD AF 7B 00 00 00  0x7BAFFDDF28
NUD:FFFFFFE4DE6A7E80| FF FF FF FF FF FF FF FF  0xFFFFFFFFFFFFFFFF
NUD:FFFFFFE4DE6A7E88| 30 31 68 99 9F FF FF FF  0xFFFFFF9F99683130 \\vmlinux\Global\el0_svc_naked+0x24
NUD:FFFFFFE4DE6A7E90| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E98| 08 00 00 00 00 00 00 00  0x8
NUD:FFFFFFE4DE6A7EA0| FF FF FF FF FF FF FF FF  0xFFFFFFFFFFFFFFFF
NUD:FFFFFFE4DE6A7EA8| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7EB0| 00 00 00 A0 00 00 00 00  0xA0000000
NUD:FFFFFFE4DE6A7EB8| 3C 6B 77 2B 46 76 A8 C2  0xC2A876462B776B3C
NUD:FFFFFFE4DE6A7EC0| 45 00 00 00 00 00 00 00  0x45        // x0 
NUD:FFFFFFE4DE6A7EC8| 80 37 81 AF 7B 00 00 00  0x7BAF813780    // x1
NUD:FFFFFFE4DE6A7ED0| 16 00 00 00 00 00 00 00  0x16       // x2
NUD:FFFFFFE4DE6A7ED8| 40 00 00 00 00 00 00 00  0x40       // x3
NUD:FFFFFFE4DE6A7EE0| 20 DD FD AF 7B 00 00 00  0x7BAFFDDD20   // x4
NUD:FFFFFFE4DE6A7EE8| 14 00 00 00 00 00 00 00  0x14       // x5
NUD:FFFFFFE4DE6A7EF0| C8 DC FD AF 7B 00 00 00  0x7BAFFDDCC8  // x6
NUD:FFFFFFE4DE6A7EF8| 01 00 00 00 00 00 00 00  0x1     // x7
NUD:FFFFFFE4DE6A7F00| CE 00 00 00 00 00 00 00  0xCE     // x8
NUD:FFFFFFE4DE6A7F08| 02 00 00 00 00 00 00 00  0x2   // x9
NUD:FFFFFFE4DE6A7F10| 00 00 00 00 00 00 00 00  0x0   // x10
NUD:FFFFFFE4DE6A7F18| 00 00 00 00 00 00 00 00  0x0   // x11
NUD:FFFFFFE4DE6A7F20| B9 E6 3B AF 7B 00 00 00  0x7BAF3BE6B9  // x12
NUD:FFFFFFE4DE6A7F28| 40 01 00 00 00 00 00 00  0x140    // x13
NUD:FFFFFFE4DE6A7F30| 0D 00 00 00 00 00 00 00  0x0D   // x14
NUD:FFFFFFE4DE6A7F38| AB AA AA AA AA AA AA AA  0xAAAAAAAAAAAAAAAB // x15
NUD:FFFFFFE4DE6A7F40| 98 CF E2 B1 7B 00 00 00  0x7BB1E2CF98 // x16
NUD:FFFFFFE4DE6A7F48| 14 B3 AB B2 7B 00 00 00  0x7BB2ABB314 // x17
NUD:FFFFFFE4DE6A7F50| D6 00 00 00 00 00 00 00  0xD6       // x18
NUD:FFFFFFE4DE6A7F58| 78 DD FD AF 7B 00 00 00  0x7BAFFDDD78  // x19
NUD:FFFFFFE4DE6A7F60| C0 72 81 AF 7B 00 00 00  0x7BAF8172C0   // x20
NUD:FFFFFFE4DE6A7F68| 16 00 00 00 00 00 00 00  0x16       // x21
NUD:FFFFFFE4DE6A7F70| 28 8D E2 B1 7B 00 00 00  0x7BB1E28D28   // x22
NUD:FFFFFFE4DE6A7F78| C0 83 84 AF 7B 00 00 00  0x7BAF8483C0  // x23
NUD:FFFFFFE4DE6A7F80| 70 83 84 AF 7B 00 00 00  0x7BAF848370  // x24
NUD:FFFFFFE4DE6A7F88| B8 83 84 AF 7B 00 00 00  0x7BAF8483B8 // x25
NUD:FFFFFFE4DE6A7F90| 80 D7 80 AF 7B 00 00 00  0x7BAF80D780 // x26
NUD:FFFFFFE4DE6A7F98| 10 B0 90 B1 7B 00 00 00  0x7BB190B010 // x27
NUD:FFFFFFE4DE6A7FA0| 17 4A A6 B1 7B 00 00 00  0x7BB1A64A17 // x28
NUD:FFFFFFE4DE6A7FA8| 10 DD FD AF 7B 00 00 00  0x7BAFFDDD10 // x29
NUD:FFFFFFE4DE6A7FB0| 30 B3 AB B2 7B 00 00 00  0x7BB2ABB330  // x30
NUD:FFFFFFE4DE6A7FB8| 10 DD FD AF 7B 00 00 00  0x7BAFFDDD10  // sp
NUD:FFFFFFE4DE6A7FC0| C4 B7 AF B2 7B 00 00 00  0x7BB2AFB7C4  // pc
NUD:FFFFFFE4DE6A7FC8| 00 00 00 60 00 00 00 00  0x60000000 // cprs
NUD:FFFFFFE4DE6A7FD0| 45 00 00 00 00 00 00 00  0x45
NUD:FFFFFFE4DE6A7FD8| CE 00 00 00 00 00 00 00  0xCE
NUD:FFFFFFE4DE6A7FE0| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7FE8| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7FF0| C8 C3 39 00 EA 71 00 00  0x71EA0039C3C8
NUD:FFFFFFE4DE6A7FF8| 13 00 00 00 00 00 00 00  0x13
NUD:FFFFFFE4DE6A8000| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A8008| 00 00 00 00 00 00 00 00  0x0
 
여기서 아주 주의 깊게 쳐야 볼 레지스터는 X8인데요. 0xCE란 값을 가르키고 있네요. 이게 뭐냐면 시스템 콜 번호이거든요. 시스템 콜 번호는 CrashTool로 확인이 가능해요. 자 확인해 볼까요? sys_sendto란 시스템 콜의 번호는 0xce임을 알 수 있어요.
crash64> sys -c
NUM  SYSTEM CALL                FILE AND LINE NUMBER
  0  sys_io_setup               ../kernel/fs/aio.c: 1257
// .. 생략
 cd  sys_getpeername            ../kernel/net/socket.c: 1794
 ce  sys_sendto                 ../kernel/net/socket.c: 1826
 cf  sys_recvfrom               ../kernel/net/socket.c: 1889
 
Reference: ARM 프로세서의 주요 기능
 
ARM 프로세서는 왜 배워야 할까
ARM 프로세서 학습하는 방법의 문제점
ARM 프로세서 소개  
ARM 아키텍처를 구성하는 주요 기능
   ● 어셈블리 명령어란  
   ● ARM의 동작 모드와 익셉션 레벨   
 
Written by <디버깅을 통해 배우는 리눅스 커널의 구조와 원리> 저자