본문 바로가기

Research/Reverse

ARM 에서는 current instruction 과 PC 가 같지 않다.




명령어 중 이런게 있었다


0x8448 mov r3, pc

0x844a mov r4, pc


r3 에는 0x8448 이, r4 에는 0x844a 가 들어갈 것 같았는데

실제로는 r3 에는 0x844c, r4 에는 0x844e 가 들어갔다.




조금 찾아보니 pc 값은 thumb mode 에서는 항상 +4 byte , arm mode 에서는 +8 byte 라고 하는데..

(http://www.iamroot.org/xe/Kernel_8_ARM/49399)

그래도 pc값을 저장하는 시점에서 + 4byte 값이 들어간다는 것이 이해가 안되었다.





영어 자료에서 찾게 된 내용은 이렇다.


ARM 파이프라인에는 3 단계가 있어서, PC는 항상 fetched 된 인스트럭션을 가리키고 있습니다. 명령어가 해석되는 단계에서는 PC-4, 그리고 실행되는 단계에서는(디버거에서는 현재 인스트럭션이라고 나타나는) PC-8 값을 갖게 됩니다.


 good explanation, and good for citing the ARM ARM. In the ARM 3-stage pipeline, the PC always points to the instruction being fetched, and PC-4 points to the instruction being decoded, and PC-8 is the "current instruction", i.e. the instruction being executed. This is also why exceptions must adjust the LR value before returning. As you noted, this applies to ARM (32-bit) instructions, hence the 4-byte adjustment per pipeline stage

- 출처 : http://stackoverflow.com/questions/2102921/strange-behaviour-of-ldr-pc-value


으아!




조금 더 찾아보니... 국내 블로그 중에서도 이런 주옥같은 내용이.



3. Allocating Stack and Saving Usr Mode Context

arch/arm/kernel/entry-common.S:

        .align  5
ENTRY(vector_swi)
        sub     sp, sp, #S_FRAME_SIZE

|  .  |
|  .  |
+-----+ <----- sp
|  .  |         |
|  .  |         |
|  .  |         |
|  .  |         |
|  .  |        pt_regs (18 * sizeof(long))
|  .  |         |
|  .  |         |
|  .  |         |
|  .  |         v
+-----+ <----- sp
|  .  |
|  .  |

        stmia   sp, {r0 - r12}              @ Calling r0 - r12
 ARM(   add     r8, sp, #S_PC           )
 ARM(   stmdb   r8, {sp, lr}^           )   @ Calling sp, lr
        mrs     r8, spsr                    @ called from non-FIQ mode,
                                            @ so ok.
        str     lr, [sp, #S_PC]             @ Save calling PC
        str     r8, [sp, #S_PSR]            @ Save CPSR
        str     r0, [sp, #S_OLD_R0]         @ Save OLD_R0
        ...

|    .     |
|    .     |
+----------+
| r0_orig  |
+----------+
| spsr_svc |
+----------+
|  lr_svc  |
+----------+
|  lr_usr  |
+----------+
|  sp_usr  |
+----------+
|   r12    |
+----------+
|    .     |
|    .     |
+----------+
|    r0    |
+----------+ <---- sp
|    .     |
|    .     |

spsr_svc : 예외발생 이전의 usr 모드에서 사용중이던 cpsr 의 값이 spsr_svc 에 저장되어있음.
lr_svc : 예외발생 이전의 usr 모드에서 사용중이던 pc의 값 (lr_svc = CPSR.T == O ? pc - 4 : pc - 2) 이 lr_svc 에 저정되어 있음. swi 는 decode 단계에서 발생하므로 pc–4를 하면 swi를 일으킨 명령 바로 다음의 주소를 얻게 된다.

pc      fetch
pc - 4  decode
pc - 8  execute

위 표는 fetch -> decode -> execute 로 이어지는 파이프라인 특성때문에 pc 값이 fetch 단계의 주소를 가리키고 있어 pc 를 읽어 들이는 실제 명령수행 단계에서는 ARM 모드의 경우 8byte, Thumb 모드에서는 4 바이트만큼 항상 앞서 있다.

r0_orig : 시그널 처리시에 시스템콜을 다시 실행해야 하는 경우 r0의 값을 다시 복구하기 위해 사용됨.



우와... 놀라운 정리. 출처는 http://pr1mary.blogspot.kr/2013/11/how-arm-supervisor-call-exception.html

정말 감사합니다.


그래서 이후 pc 갖고 계산할 때는 파이프라인 따져서, 명령어 + 2줄 뒤 로 본다.