참고. THUMB 코드와 ARM 코드 및 상호 호출(Interworking)

원문 : http://kkamagui.springnote.com/pages/437975

 

들어가기 전에...

 

0.시작하면서...

 NDS 개발 툴킷인 devkitPro를 보면 C 코드 같은 경우 THUMB 코드를 사용하도록 되어있다. 하지만 인터럽트가 발생하면 ARM 모드로 전환되고, 기타 ARM 코드로 생성된 라이브러리나 어셈블리로 짜여진 ARM 코드 등등 같은 것을 같이 호출하여 사용하기위해 interwork 옵션으로 중간에 proxy 함수(베니어 함수-veneer)를 사용하여 이를 처리한다.

 Proxy 코드는 어떤식으로 생기는 것일까? NDS 커널에 간단한 테스트를 추가하여 이를 확인해 보기로 했다.  NDS에서 C 코드는 기본적으로 Thumb 모드로 컴파일 된다. 하지만 어셈블리어 코드는 ARM 모드로 컴파일한다. 이 두 언어의 코드를 함께 빌드한 후, 역어셈블리(Disassembly)해보면 어떤 식으로 호출되는지 알 수 있다.

NDS 커널에 대한 내용은 21 NDS 커널(Kernel) 만들기를 참고하도록 하자.

 

1.THUMB 코드와 ARM 코드 작성

NDS 커널에 간단하게 아래의 루틴을 추가하였다.

  1. <main.cpp 파일>
  2. ...... 생략 ......
  3. extern "C" void Test3( void ); <== assem.s 파일애서 만든 ARM 코드 함수
  4. ...... 생략 ......
  5. int main()
  6. {
  7. ...... 생략 ......
  8.     Test3();
  9. ...... 생략 ......
  10. }

 

  1. <assem.s 파일>
  2.     .global Test3 

  3. ...... 생략 ......
  4. Test3:
        bx lr 

 위의 코드를 보면 알 수 있듯이 C에서 간단히 함수를 호출하고 어셈블리어에서는 그냥 리턴하도록 구현되어있다.

 

2.결과 파일 디스어셈블리(Disassembly)

2.1 테스트 함수 디스어셈블리(THUMB->ARM 호출)

이것을 컴파일 및 링크하여 나온 결과를 objdump.exe로 디스어셈블리 해보면 아래와 같은 결과를 볼 수 있다.

  1. 0200128c <main>:

    ...... 생략 ......

     20012d6: f014 fdb3  bl 2015e40 <__Test3_from_thumb>

    ...... 생략 ......

     

    02015e40 <__Test3_from_thumb>:
     2015e40: 4778       bx pc 2015e42: 46c0       nop   (mov r8, r8)

     

  2. 02015e44 <__Test3_change_to_arm>: 2015e44: eaffafbd  b 2001d40 <Test3>

     

    02001d40 <Test3>: 2001d40: e12fff1e  bx lr 2001d44: e1a00000  nop   (mov r0,r0)
     2001d48: e1a00000  nop   (mov r0,r0)
     2001d4c: e1a00000  nop   (mov r0,r0)

 ARM 코드를 바로 호출하는게 중간에 THUMB 모드에서 ARM 모드로 전환하는 코드를 통해 호출하고 있는 것을 볼 수 있다. BX 명령은 오퍼런드(Operand)의 0bit 값을 CPSR의 THUMB 모드 비트로 설정해 주는 역할을 하는 명령어이다. 즉 오퍼런드의 0bit가 1이면 Thumb 모드가 되고 0이면 ARM 모드가 된다. 명령어에 대한 자세한 내용은 참고. ARM 어셈블리(Assembly)를 참고하도록 하자.

 이제 main 함수부터 순차적으로 따라가보자. 맨 위에 <main>에서 bl 2014e40을 실행하면 __Test3_from_thumb() 함수가 호출되고, bx pc를 호출하면 PC는 항상 현재 실행하는 명령 + ( 2 * 명령어 크기 )의 위치에 있으므로(즉 짝수 이므로), bx pc를 하는 순간 ARM 모드로 전환된다. 그리고 PC는 현재 실행 중인 주소(2015e40) + 4byte( 2 * Thumb 모드 명령어 크기(2) ) 다음에 있는 b 2001d40을 실행하게 된다. 이렇게 하여 무사히 Thumb -> ARM으로 호출되었다.

 그런데 뭔가 좀 이상한 느낌이 들지 않는가? BL 명령으로 __Test3_from_thumb 을 호출하고 나면 BX PC 명령에 의해 코드가 4Byte 또는 2Byte로 정렬되어있기 때문에 ARM 모드로 전환된다. 이러한 상태에서 ARM 코드를 정상적으로 실행한 뒤에 BX LR 명령으로 원래 루틴으로 돌아가는데... 정상적으로 다시 실행이 되려면 lr에 들어있는 0번째 bit가 1로 설정되어있어야 한다는 말이 된다.

 실제로 그럴까? ARM 문서에 보면 그냥 LR에 들어가는 값은 현재 코드 위치 + 4라고만 되어있었다. 이 설명 만으로는 BX LR 을 수행했을 때 THUMB 모드로 정상적으로 돌아갈 수 없다. 그래서 테스트 코드에서 LR값을 받아 찍어보았다.

 

 결과는 원래 리턴할 위치에 1값이 더 더해져 있었다. 원래 LR값은 0x20012D6에 4가 더해져서 0x20012DA가 되어야 한다. 그러나 실제 들어있는 값은 0x20012DB가 들어있었다. 이것은 결국 THUMB 모드의 BL 같은 경우 LR에 PC + 4 + 1의 값을 넣는다는 것을 의미했다. @0@)/~!!! 뭐 여튼 알아냈으니 다행... ARM모드의 경우는 BL 시에 LR의 값은 PC + 8이다.

 아래는 ARM Architecture Manual에서 찾은 THUMB 모드에서 BL, BLX에 대한 내용이다.

bl1.PNG

bl2.PNG

 

 

 

2.2 타이머 함수 디스어셈블리(ARM->THUMB)

 그럼 이번에는 반대의 경우를 보자. 인터럽트 핸들러 함수인 타이머 함수 핸들러 같은 경우 ARM 코드로 되어있다. 하지만 스케줄러를 호출하는 함수 같은 경우는 C로 된 함수를 호출하여 그 안에서 처리한다. 이 부분에 대한 코드는 아래와 같다.

  1. 2001c40 <isrTimerInAsm>: 2001c40: e92d4000  stmdb sp!, {lr}
     2001c44: eb00507a  bl 2015e34 <__isrTimerInC_from_arm> 2001c48: e59f00bc  ldr r0, [pc, #188] ; 2001d0c <g_dwCurTask>
     2001c4c: e59f10c0  ldr r1, [pc, #192] ; 2001d14 <g_dwNextTask>
     2001c50: e3510000  cmp r1, #0 ; 0x0
     2001c54: 0a000001  beq 2001c60 <TIMEREND>
     2001c58: e28d2004  add r2, sp, #4 ; 0x4
     2001c5c: eb000001  bl 2001c68 <SwitchTask2>
  2. 02015e34 <__isrTimerInC_from_arm>:
     2015e34: e59fc000  ldr ip, [pc, #0] ; 2015e3c <__isrTimerInC_from_arm+0x8>
     2015e38: e12fff1c  bx ip
     2015e3c: 020013fd  andeq r1, r0, #-201326589 ; 0xf4000003
  3. 020013fc <isrTimerInC>: 20013fc: b510       push {r4, lr}
     20013fe: b084       sub sp, #16
  4. ...... 생략 ......
  5.  2001466: b004       add sp, #16
     2001468: bd10       pop {r4, pc}

 위의 코드에서 보면 역시나 BX IP에서 IP 레지스터가 가리키는 값이 0x20013FDisrTimerInC의 주소 + 1로 설정되어있음을 알 수 있다. isrTimerInC 함수에서 lr 레지스터를 꺼내어 PC에 넣는 순간 역시 BX와 마찬가지로 레지스터의 0 bit가 CPSR의 Thumb 비트에 설정된다.

 

3.마치며...

 ARM 코드와 THUMB 코드를 서로 호출할 때 어떠한 일이 일어나는지 확인해 보았다. 모드가 여러종류가 존재하다보니 이러한 복잡한 부분이 생기는 것 같은데... 한번 봐두는 것도 나쁘지 않은 것 같다. THUMB 모드의 명령어와 ARM 모드의 명령어의 동작이 약간씩 다르므로 익혀두는 것도 나쁘지 않은 것 같다.

 ARM을 열심히 하도록 하자 @0@)/~

 

 

 

 

 

 

 

 

 

이 글은 스프링노트에서 작성되었습니다.

+ Recent posts