아래와 같은 콜 스택에서 유저 공간에서 돌던 레지스터 Stack Push와 Exception 발생 시 Stack Push에 대해서 살펴봤어요. 이제는 평상시 함수 호출 시 어떻게 Stack Push를 하는 지 점검하려고 해요.
자 계속 그 동안 다뤄왔던 아래 "rild"란 프로세스의 콜스택에서 rpi_ipc_router_sendmsg()-> rpi_ipc_router_send_to() 으로 함수가 호출된 후 스택 푸쉬가 어떻게 수행되는 지 점검해볼께요.
-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|rpi_ipc_router_write_pkt(inline)
-006|rpi_ipc_router_send_to()
-007|rpi_ipc_router_sendmsg()
-008|sock_sendmsg_nosec(inline)
-008|sock_sendmsg()
-009|SYSC_sendto(inline)
-009|sys_sendto()
-010|el0_svc_naked(asm)
rpi_ipc_router_sendmsg()함수 내에서 rpi_ipc_router_send_to() 함수를 호출하기 직전 ARM Instruction을 살펴 볼께요. 0xFFFFFF9F9A81C2A4주소가 rpi_ipc_router_send_to() 함수를 호출하거든요.
이 때 스택 주소(SP)는 0xFFFFFFE4DE6A7CA0이에요.
아래 코드를 보면 각각 파라미터로 입력하는 값들을 확인할 수 있는데요.
x0 = x23, x1 = x20, x3 = x29+0x88, x3 = x28
NSX:FFFFFF9F9A81C294|AA1703E0 mov x0,x23
NSX:FFFFFF9F9A81C298|AA1403E1 mov x1,x20
NSX:FFFFFF9F9A81C29C|910223A2 add x2,x29,#0x88 ; x2,x29,#136
NSX:FFFFFF9F9A81C2A0|AA1C03E3 mov x3,x28
NSX:FFFFFF9F9A81C2A4|97FFF7A3 bl 0xFFFFFF9F9A81A130 ; rpi_ipc_router_send_to
자 이제, rpi_ipc_router_send_to() 함수 호출이 되는 순간 어떤 짓을 할까요? 좀 살펴 볼께요.
NSX:FFFFFF9F9A81A130|A9B67BFD rpi_ipc_router_send_to: stp x29,x30,[SP,#-0xA0]! ; x29,x30,[SP,#-160]! //<<--[1]
NSX:FFFFFF9F9A81A134|910003FD mov x29,SP
NSX:FFFFFF9F9A81A138|A90153F3 stp x19,x20,[SP,#0x10] ; x19,x20,[SP,#16] //<<--[2]
NSX:FFFFFF9F9A81A13C|A9025BF5 stp x21,x22,[SP,#0x20] ; x21,x22,[SP,#32]//<<--[3]
NSX:FFFFFF9F9A81A140|A90363F7 stp x23,x24,[SP,#0x30] ; x23,x24,[SP,#48]
NSX:FFFFFF9F9A81A144|A9046BF9 stp x25,x26,[SP,#0x40] ; x25,x26,[SP,#64]
NSX:FFFFFF9F9A81A148|A90573FB stp x27,x28,[SP,#0x50] ; x27,x28,[SP,#80]//<<--[4]
[1] "stp x29,x30,[SP,#-0xA0]!"
이게 무슨 명령어이냐면, 스택 공간을 더 잡아주는 명령어에요. 이 명령어가 수행되면 스택 주소가 FFFFFFE4DE6A7C00로 바뀌게 되죠.
어떻게 변경되냐구요? FFFFFFE4DE6A7C00=FFFFFFE4DE6A7CA0(기존스택주소)- 0xA0
그리고, 동시에 x29, x30을 스택 주소와 스택 주소+0x8 위치에 Push를 해줘요.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0 //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF 0xFFFFFF800921C2A8 \\vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0 // <<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C18| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C38| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C40| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C48| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C50| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF 0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48
[2] "stp x19,x20,[SP,#0x10] ; x19,x20,[SP,#16]"
스택 주소 FFFFFFE4DE6A7C00에서 0x10만큼 떨어진 주소에 x19와 x20을 푸쉬하네요.
NSD:FFFFFFE4DE6A7BF8| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0 // 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF 0xFFFFFF800921C2A8 \\vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0
NSD:FFFFFFE4DE6A7C10| 19 00 00 00 00 00 00 00 0x0 // <<--x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF 0xFFFFFFE4DF968E80 //<<--x20
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00 0x0
[3] "stp x21,x22,[SP,#0x20] ; x21,x22,[SP,#32]"
스택 주소 FFFFFFE4DE6A7C00에서 0x20만큼 떨어진 주소에 x21와 x22을 푸쉬하네요.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF 0xFFFFFF800921C2A8 \\vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0
NSD:FFFFFFE4DE6A7C10| 19 00 00 00 00 00 00 00 0x19 \\vmlinux\Global\sha1_ce_offsetof_count+0x1
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF 0xFFFFFFE4DF968E80
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF 0xFFFFFFE54FC61500 //<<--x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF 0xFFFFFFE556F4D080 //<<--x22
[4] 이제 스택 푸쉬가 끝났어요. 어떤 값들인지 비교해볼까요?
[After]
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0 //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 81 9A 9F FF FF FF 0xFFFFFF9F9A81C2A8 .vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0 //<<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00 0x0 //<<-- x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF 0xFFFFFFE4DF968E80 //<-- x20
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF 0xFFFFFFE54FC61500 //<-- x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF 0xFFFFFFE556F4D080 //<-- x22
NSD:FFFFFFE4DE6A7C30| 00 70 C6 4F E5 FF FF FF 0xFFFFFFE54FC67000 //<-- x23
NSD:FFFFFFE4DE6A7C38| 16 00 00 00 00 00 00 00 0x16 //<-- x24
NSD:FFFFFFE4DE6A7C40| 38 00 00 00 00 00 00 00 0x38 //<-- x25
NSD:FFFFFFE4DE6A7C48| 16 00 00 00 00 00 00 00 0x16 //<-- x26
NSD:FFFFFFE4DE6A7C50| 02 00 00 00 00 00 00 00 0x2 //<-- x27
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00 0x0 //<-- x28
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF 0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48
[Before]
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0 //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF 0xFFFFFF800921C2A8 \\vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0 // <<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C18| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C38| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C40| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C48| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C50| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00 0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF 0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48
자 다시, rpi_ipc_router_send_to() 함수를 호출하기 직전 FFFFFF9F9A81C2A4 주소로 되돌아 가 볼께요.
아래와 같이 파라미터를 저장했지오.
x0 = x23, x1 = x20, x3 = x29+0x88, x3 = x28
NSX:FFFFFF9F9A81C294|AA1703E0 mov x0,x23
NSX:FFFFFF9F9A81C298|AA1403E1 mov x1,x20
NSX:FFFFFF9F9A81C29C|910223A2 add x2,x29,#0x88 ; x2,x29,#136
NSX:FFFFFF9F9A81C2A0|AA1C03E3 mov x3,x28
NSX:FFFFFF9F9A81C2A4|97FFF7A3 bl 0xFFFFFF9F9A81A130 ; rpi_ipc_router_send_to
그리고 rpi_ipc_router_send_to() 함수 호출 후 스택에 푸쉬된 값을 보면, x23은 0xFFFFFFE54FC67000 x20은 0xFFFFFFE4DF968E80 이거든요.
그런데 이 x23은 x0즉 첫 번 째 파라미터로 전달을 하고, x20은 두 번 째 파라미터로 전달을 했죠.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF 0xFFFFFFE4DE6A7CA0 //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 81 9A 9F FF FF FF 0xFFFFFF9F9A81C2A8 .vmlinux\ipc_router_socket\rpi_ipc_router_sendmsg+0x2C0 //<<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00 0x0 //<<-- x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF 0xFFFFFFE4DF968E80 //<-- x20
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF 0xFFFFFFE54FC61500 //<-- x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF 0xFFFFFFE556F4D080 //<-- x22
NSD:FFFFFFE4DE6A7C30| 00 70 C6 4F E5 FF FF FF 0xFFFFFFE54FC67000 //<-- x23
NSD:FFFFFFE4DE6A7C38| 16 00 00 00 00 00 00 00 0x16 //<-- x24
NSD:FFFFFFE4DE6A7C40| 38 00 00 00 00 00 00 00 0x38 //<-- x25
NSD:FFFFFFE4DE6A7C48| 16 00 00 00 00 00 00 00 0x16 //<-- x26
NSD:FFFFFFE4DE6A7C50| 02 00 00 00 00 00 00 00 0x2 //<-- x27
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00 0x0 //<-- x28
이제 함수 호출 시 스택 푸쉬가 되는 규칙을 활용해서 디버깅을 좀 해볼까 해요.
이제 소스 코드를 열어 보아야 할 것 같은데요.
rpi_ipc_router_send_to() 함수 호출 시 전달되는 파라미터의 타입을 좀 더 자세히 알 수 있네요. 정리하면..
x0(x23) = 0xFFFFFFE54FC67000 = struct rpi_ipc_port *src, x1(x20) = 0xFFFFFFE4DF968E80 = struct sk_buff_head *out_skb_head
int rpi_ipc_router_send_msg(struct rpi_ipc_port *src,
struct rpi_ipc_addr *dest,
void *data, unsigned int data_len)
{
struct sk_buff_head *out_skb_head;
int ret;
out_skb_head = rpi_ipc_router_buf_to_skb(data, data_len);
//.. 생략..
ret = rpi_ipc_router_send_to(src, out_skb_head, dest, 0);
그럼 전달되는 파라미터는 확인해보면 아래와 같아요.
첫번 째 파라미터 (struct rpi_ipc_port*)
v.v %s %y %t (struct rpi_ipc_port*)0xFFFFFFE54FC67000
(struct rpi_ipc_port *) (struct rpi_ipc_port*)0xFFFFFFE54FC67000 = 0xFFFFFFE54FC67000 = -> (
(struct list_head) list = (
(struct list_head *) next = 0xFFFFFF9F9C19D1E0 = local_ports[5],
(struct list_head *) prev = 0xFFFFFFE5775EC200 = ),
(struct kref) ref = ((atomic_t) refcount = ((int) counter = 1)),
(struct rpi_ipc_port_addr) this_port = ((uint32_t) node_id = 1, (uint32_t) port_id = 197),
(struct rpi_ipc_port_name) port_name = ((uint32_t) service = 0, (uint32_t) instance = 0),
(uint32_t) type = 0,
(unsigned int) flags = 0,
(struct mutex) port_lock_lhc3 = ((atomic_t) count = ((int) counter = 1), (spinlock_t) wait_lock
(struct comm_mode_info) mode_info = ((int) mode = 0, (void *) xprt_info = 0x0 = ),
(struct rpi_ipc_port_addr) dest_addr = ((uint32_t) node_id = 0, (uint32_t) port_id = 0),
(int) conn_status = 0,
(struct list_head) port_rx_q = ((struct list_head *) next = 0xFFFFFFE54FC67078 = , (struct list_
(struct mutex) port_rx_q_lock_lhc3 = ((atomic_t) count = ((int) counter = 1), (spinlock_t) wait_
(char [32]) rx_ws_name = "ipc000000c5_1490_rild",
(struct wakeup_source *) port_rx_ws = 0xFFFFFFE54FC60100 = ,
두번 째 파라미터 (struct sk_buff_head*)
v.v %y %s %h (struct sk_buff_head*)0xFFFFFFE4DF968E80
(struct sk_buff_head*)0xFFFFFFE4DF968E80 = 0xFFFFFFE4DF968E80 = -> (
next = 0xFFFFFFE54FC61500 = ,
prev = 0xFFFFFFE54FC61500 = ,
qlen = 0x1,
lock = (rlock = (raw_lock = (owner = 0x1, next = 0x1))))
이런 방식으로 디버깅을 할 수 있어요. 이게 모두 ARM64 아키텍쳐에서 함수호출 시 스택 Push가 어떻게 수행되는지 알았기 때문에 가능한 것이지오. 참 쉽죠?
# Reference (ARM Processor)
# Reference: For more information on 'Linux Kernel';
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
'시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 카테고리의 다른 글
ARM64 - 프로세스(Process) preempt_disable(), preemption 스케줄(Schedule) 조건 분석 (0) | 2023.06.09 |
---|---|
ARM64 - ttrb0 Special Register 설정 건 (0) | 2023.06.09 |
ARM64- 스택 푸쉬(Stack Push) Userspace -> Kernel Space (0) | 2023.06.09 |
ARM64(Aarch64) - Special Register 설정(Trace32) (0) | 2023.06.09 |
ARM32- 스택 푸쉬(Stack Push) Userspace -> Kernel Space (0) | 2023.06.09 |