tst 명령어는 연산자와 비연산자 사이 AND 비트 연산을 수행합니다.
AND 비트 연산 결과에 따라 CPSR 레지스터 Z 비트는 다음과 같이 변경됩니다.
Z: 0 ( AND 비트 연산 결과가 1인 경우)
Z: 1 ( AND 비트 연산 결과가 0인 경우)
tst 명령어가 위와 같이 동작하는지 증명하기 위해 T32 디버거를 실행해 보겠습니다.
다음 화면은 tst 명령어를 실행하기 직전입니다.
NSR:80107E6C|ret_to_user_from_irq: ldr r2,[r9,#0x8]
NSR:80107E70| cmp r2,#0x7F000000 ; r2,#2130706432
NSR:80107E74| blne 0x8010B5FC ; addr_limit_check_failed
NSR:80107E78| ldr r1,[r9]
NSR:80107E7C|_____________________________tst_____r1,#0x0F_________;_r1,#15
NSR:80107E80| bne 0x80107E48 ; slow_work_pending
NSR:80107E84|no_work_pending: bl 0x801E9CE8 ; trace_hardirqs_on
N _ R0 0 R8 0
Z Z R1 2 R9 0
C _ R2 0 R10 0
V _ R3 0 R11 0
Q _ R4 00020000 R12 0
R5 0 R13 C000D000
0 _ R6 0 R14 0
1 _ R7 0 PC 80107E7C
2 _ SPSR 10 CPSR 400001D3
R1가 2(이진수 10)이니 0xF와 AND 비트 연산을 하면 1이 될 것입니다.
1111 (0xF)
10 (2: R1)
------
1 (연산 결과)
다음은 "tst r1,#0x0F" 명령어를 실행한 후 결과입니다.
NSR:80107E6C|ret_to_user_from_irq: ldr r2,[r9,#0x8]
NSR:80107E70| cmp r2,#0x7F000000 ; r2,#2130706432
NSR:80107E74| blne 0x8010B5FC ; addr_limit_check_failed
NSR:80107E78| ldr r1,[r9]
NSR:80107E7C| tst r1,#0x0F ; r1,#15
NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending
NSR:80107E84|no_work_pending: bl 0x801E9CE8 ; trace_hardirqs_on
N _ R0 0 R8 0
Z _ R1 2 R9 0
C _ R2 0 R10 0
V _ R3 0 R11 0
Q _ R4 00020000 R12 0
R5 0 R13 C000D000
0 _ R6 0 R14 0
1 _ R7 0 PC 80107E80
2 _ SPSR 10 CPSR 01D3
여기서 눈을 크게 뜨고 봐야 할 중요한 정보는 ARM CPSR 레지스터입니다.
CPSR 레지스터가 0x1D3으로 변경됐습니다. CPSR 레지스터 Z 비트가 0이란 이야기입니다.
bne 명령어는 'branch not equal'이란 뜻입니다. Z가 0이면 브랜치하겠다는 의미입니다.
bne 0x80107E48 ;slow_work_pending
CPSR 레지스터 Z 비트가 0이니 slow_work_pending 레이블로 브랜치 할 것입니다.
NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending
이 결과를 예상하면서 위 "bne 0x80107E48 ;slow_work_pending" 코드를 실행하니
slow_work_pending 레이블로 브랜치했습니다.
NSR:80107E48|E1A0000D__slow_work_pending:__cpy_____r0,r13
NSR:80107E4C|E1A02008 cpy r2,r8
NSR:80107E50|EB000D82 bl 0x8010B460 ; do_work_pending
NSR:80107E54|E3500000 cmp r0,#0x0 ; r0,#0
NSR:80107E58|0A000009 beq 0x80107E84 ; no_work_pending
NSR:80107E5C|B3A07000 movlt r7,#0x0 ; r7,#0
NSR:80107E60|E89D007F ldm r13,{r0-r6}
NSR:80107E64|EA000032 b 0x80107F34 ; local_restart
이번엔 r1 레지스터가 0x1000 일 때 tst 명령어가 어떤 동작을 하는지 확인해봅시다.
NSR:80107E6C|ret_to_user_from_irq: ldr r2,[r9,#0x8]
NSR:80107E70| cmp r2,#0x7F000000 ; r2,#2130706432
NSR:80107E74| blne 0x8010B5FC ; addr_limit_check_failed
NSR:80107E78| ldr r1,[r9]
NSR:80107E7C|_____________________________tst_____r1,#0x0F_________;_r1,#15
NSR:80107E80| bne 0x80107E48 ; slow_work_pending
NSR:80107E84|no_work_pending: bl 0x801E9CE8 ; trace_hardirqs_on
N _ R0 0 R8 0
Z _ R1 1000 R9 0
C _ R2 0 R10 0
V _ R3 0 R11 0
Q _ R4 00020000 R12 0
R5 0 R13 C000D000
0 _ R6 0 R14 0
1 _ R7 0 PC 80107E7C
2 _ SPSR 10 CPSR 01D3
위 명령어를 실행하기 전 CPSR 레지스터는 0x1D3이고 R1이 0x1000이란 사실을 기억합시다.
0xF는 이진수로 1111이고 R1가 저장하고 있는 0x1000은 이진수로 1111_0000_0000_0000 일 것입니다.
따라서 AND 비트 연산 결과는 0일 것입니다.
1111_0000_0000_0000 ( 0x1000: R1)
1111 ( 0xF)
--------------------------------------------- AND 연산
0 (결과)
이 사실을 염두해두고 "tst r1,#0x0F" 명령어를 T32로 실행하겠습니다.
결과 화면은 다음과 같습니다.
NSR:80107E6C|ret_to_user_from_irq: ldr r2,[r9,#0x8]
NSR:80107E70| cmp r2,#0x7F000000 ; r2,#2130706432
NSR:80107E74| blne 0x8010B5FC ; addr_limit_check_failed
NSR:80107E78| ldr r1,[r9]
NSR:80107E7C| tst r1,#0x0F ; r1,#15
NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending
NSR:80107E84|no_work_pending: bl 0x801E9CE8 ; trace_hardirqs_on
[레지스터 세트]
N _ R0 0 R8 0
Z Z R1 1000 R9 0
C _ R2 0 R10 0
V _ R3 0 R11 0
Q _ R4 00020000 R12 0
R5 0 R13 C000D000
0 _ R6 0 R14 0
1 _ R7 0 PC 80107E80
2 _ SPSR 10 CPSR 400001D3
CPSR 레지스터가 400001D3 이고 Z 비트가 1이 됐습니다.
레지스터 세트에서 붉은색으로 표시된 부분을 눈으로 따라가 보시기 바랍니다.
N _ R0 0 R8 0
Z Z R1 1000 R9 0
Z 필드가 켜져 있습니다.
Z 비트가 1인데 다음 bne 명령어를 실행하면 어떻게 실행할까요?
NSR:80107E80|_____________________________bne_____0x80107E48_______;_slow_work_pending
NSR:80107E84|no_work_pending: bl 0x801E9CE8 ; trace_hardirqs_on
당연히 slow_work_pending 레이블로 브랜치하지 않고 80107E84 주소로 브랜치할 것입니다.
NSR:80107E6C|ret_to_user_from_irq: ldr r2,[r9,#0x8]
NSR:80107E70| cmp r2,#0x7F000000 ; r2,#2130706432
NSR:80107E74| blne 0x8010B5FC ; addr_limit_check_failed
NSR:80107E78| ldr r1,[r9]
NSR:80107E7C| tst r1,#0x0F ; r1,#15
NSR:80107E80| bne 0x80107E48 ; slow_work_pending
NSR:80107E84|no_work_pending:_____________bl______0x801E9CE8_______;_trace_hardirqs_on
# Reference: For more information on 'Linux Kernel';
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 1
디버깅을 통해 배우는 리눅스 커널의 구조와 원리. 2
'시스템 소프트웨어 개발을 위한 Arm 아키텍처의 구조와 원리' 카테고리의 다른 글
유용한 ARM 책 - 컨텐츠 (0) | 2023.06.09 |
---|---|
[ARM] CPSR(Current Program Status Register) Register (0) | 2023.06.09 |
[Linux][ARM] Coprocessor(코프로세서) Assembly (0) | 2023.06.09 |
[리눅스커널] 라즈비안: vmlinux 소스 코드 정보 추가 : CONFIG_DEBUG_INFO (0) | 2023.06.09 |
arm32 - Unwind Idx/prolog 소개 (0) | 2023.06.09 |