본문 바로가기
ARM과 Thumb 사이의 Veneer

ARM과 Thumb mode 얘기 조금 더.

Thumb mode에 진입하는 방법 있잖아요. 그 얘기 좀 더 해야겠어요. 우리 이제까지 ARM, Thumb mode 관련된 얘기를 했으니까, ARM mode에서 Thumb mode로의 진입은 어떻게 해야 되는 것이냐 하는 이야기에요.

ARM mode와 Thumb mode의 차이는 아~주 단순합니다. CPSR의 T bit가 1이냐 0이냐에 따라 Thumb이냐 ARM이냐를 Processor가 구분하죠. ARM core는 CPSR의 T bit가 1이면 Thumb mode니까 2 byte씩 명령어를 fetch 해서 실행하고요, CPSR의 T bit가 0이면 ARM mode니까 4 byte씩 명령어를 fetch해서 실행한답니다. 오, 그럼 답은 나왔네요. 각 mode로 진입하는 방법은 T bit를 원하는 대로 주물럭 하면 된다는 답이 나왔네요.

하지만, 이런 원시적인 방법 말고 T bit를 1과 0으로 자동 setting해 주는 명령어가 있어요. 그게 바로 bx라는 명령어 이죠. bx라는 명령어에 jump할 주소를 주게 되는데, 그 주소가 홀수이면 T bit를 1로, 즉 Thumb mode로, 그 주소가 짝이면 T bit를 0으로, 즉 ARM mode로 자동 setting을 해준 답니다. 우와, 간편하네요.

주소가 홀수이냐, 짝수이냐는 주소의 1의 자리가 홀수이냐 짝수이냐 라는 것만으로 결정하니까,
결국엔 주소자리의 LSB가 1이면 Thumb으로 0이면 ARM으로 간다는 거지요. 아.. 같은 얘기 하려니까, 지루하죠.

그런데, 문제는 우리가 Assembly로 직접 프로그래밍 해서 어느 주소로 jump 해라 마라 하는 루틴을 직접 짜면 좋은데, 그게 아니고 ,ARM mode의 C code랑 Thumb mode의 C code가 혼재한 상태에서 각가 compiler해서 linker로 묶죠. 그러니까 우리가 최하위 주소를 감나라 팥나라 할 수 있는 상황이 아닙니다. 그래서 나온 게 linker가 link를 할 때 이런 Code들 사이를 interworking 할 수 있는 중간 layer를 끼워 넣어 줍니다. 그게 바로 Veneer라는 건데요, 우리나라 말로는 베니아 합판 할 때 그 베니아 이죠. 베니아라는게 톱밥을 압축해서 얇은 나무판 사이에 집어 넣은 후, 척 허니 붙여서 하나의 두꺼운 합판처럼 보이게 하는 가짜 합판인데요, 그 사이에 톱밥이 바로 베니아 라고 보시면 되요.

이런 Veneer의 역할을 Compiler가 알아서 자동으로 mode전환이 되는 코드를 넣어주는 거죠. 흔히 유식한 말로는

"Arm linker는 ARM 코드와 Thumb 코드를 link 하고요, 필요 할 때는 자동적으로 processor state를 ARM과 Thumb으로 변경 할 수 있는 interworking veneer를 생성하고, veneer가 생성 된 그 곳에 branch instruction의 범위를 확장하기 위해 long branch veneers를 생성한다. Arm linker는 Arm function이 Thumb state에 호출되거나 Thumb function이 Arm state에서 호출되는 것을 구분 할 수 있고, 필요 시 processor state를 변경하기 위해 call과 return instruction을 변경 하거나 할 수 있는 veneers라고 불리는 작은 code section을 삽입한다. “
라네요.

그러니까, 결국, Veneer를 사용하면 프로그래머는 ARM상태에서 CPSR의 상태조작 없이, 또는 Bx 명령어의 주소를 어떻게 줄 거냐에 상관없이 Thumb코드를 호출할 수 있게 되는 거죠.

말로만 주저리 주저리 하니까, 잘 모르겠죠. 전체적인 줄거리만 늘어 놓으면, 아래처럼 되는 거에요. 테마는 ARM mode의 main에서 Thumb mode의 thumbveneer()라는 함수를 호출하는 과정이에요.

CODE32
main:

어쩌고 저쩌고 삐약 삐약...
blx $Ven$thumbveneer   ; thumb
bx lr

$Ven$testcode             ; Linker가 자동으로 만들어낸 Veneer입니다. 이런 Layer를
ldr r12,Veneerconstant   ; 자동으로 만들어주는 고마운 Linker
bx r12
Veneerconstant:
DCD  thumbveneer

뭐, 이런 셈인거죠.

실제로 이런가 함 보시죠.

arm.c

extern int thumbveneer(int);

int main(int ar)
{
return thumbveneer(ar);
}

thumb.c

int thumbveneer (int a1)
{
return (int)(a1<<4);
}

그럼 이 녀석들을 컴파일 해보겠스스리리리리다.

armcc -c -apcs /interwork arm.c
tcc -c -apcs /interwork thumb.c
armlink -elf -o veneer.elf arm.o thumb.o

한가지 유의할 점은 나중에 linker가 interworking할 수 있도록 /interwork option을 줘서 compile 해 주시고, apcs는 AAPCS(이전에 나왔죠 ARM register사용법이요)를 따르도록 해주는 겁니다. AAPCS 따르게 안 해주면 엉망이 된다는 거~ register 사용법이 서로 틀려서 완전 엉망 될 수도 있다는 거~ 알아 두시고요. 이렇게 해서 나온 veneer.elf를 Disassemble해서 보겠사옵니다.

그전에 thumbveneer(int)가 어떻게 assemble이 되었는지 확인해 봐야지 나중에 제대로 확인 가능하겠죠?

fromelf -c thumb.o

** Section #5 '.text' (SHT_PROGBITS) [SHF_ALLOC + SHF_EXECINSTR]
    Size   : 4 bytes (alignment 4)
    thumbveneer
    $t
    .text
        0x00000000:    0100        ..      LSL      r0,r0,#4    ; 요부분이 a1<<4의 구현
        0x00000002:    4770        pG      BX       r14

 

오호호, 그럼 fromelf -c veneer.elf해서 main함수에서 점점 흘러흘러 위의 함수까지 흘러 들어오면

제대로 온거 겠지요.

Entry point인 main을 보아야겠죠? 거기에는 분명히 Veneer로 짬푸하는 부분이 있을꺼에요.
    main
    .text
        0x000080a8:    ea00017b    {...    B        $Ven$AT$L$$thumbveneer  ; 0x869c

와랏 진짜 thumbveneer라는 Veneer 곳으로 짬푸를 하는군요. 그럼 $Ven$AT$L$$thumbveneer 이걸 찾아 볼게요.

    $Ven$AT$L$$thumbveneer
        0x0000869c:    e59fc000    ....    LDR      r12,0x86a4
        0x000086a0:    e12fff1c    ../.    BX       r12

와랏 뭔가 Load를 한 후 그 곳으로 Branch하는군요!그럼 0x86a4를 보면 되겠네요. 과연 0x86a4에는 무엇이?

        0x000086a4:    000080ad    ....    DCD    32941

오호라, 0x80ad (32941) 이라는 값이 들어 있고요. 이 값으로 bx하겠네요. 근데 주소 값이 홀수에요. 그러면, Thumb mode의 0x80ac로 짬푸가 맞는 얘기겠죠.

    .text
        0x000080ac:    0100        ..      LSL      r0,r0,#4
        0x000080ae:    4770        pG      BX       r14

꺄오, 0x80ac에 와 보니, 바로바로~ 찾았다 찾았다~ thumbveneer()함수가 있네요. 으흠~ 이런식으로 Linker가 Veneer로 잘 연결해 주는 역할을 잘 했답니다. 가교쟁이.꺄오, 증말 간딴하죠.

그런데, 이런 Veneer는 ARM ↔ Thumb 사이의 가교 역할만 하는 건 아니에요. 이전에 그런 말을 한 적이 있죠. LDR 의사 명령어에서,

" Branch 명령어의 경우 32 bit중 Branch 명령어 자체가 차지하는 9bit를 제외한 23bit를 branch하는 주소로 사용하는데, 이 23bit를 ARM 명령어로 생각해서 4 byte당 한 주소씩으로 따져 2비트 Left shift을 사용해 봤자 분기 영역은 최대로 상수 표현 범위(pc ± 32MB) 라서 필요에 따라 그이상의 분기가 필요할 때 32 bit를 fully 사용가능한 의사 명령어를 이용한답니다.Thumb에서의 Branch의 경우에는 더욱 최악이라, pc ± 4MB 밖에 되지 않기 때문에 이런 의사 명령어가 꼭 필요할 때도 있어요. "

라고 말이죠.

이런 먼 곳으로 분기 할 때 LDR명령어를 사용하듯이, Veneer도 32bit 주소를 모두 사용할 수 있는 장점이 있답니다. 당연히 LABel을 이용해서 그곳에 주소를 LDR하니까 가능한 얘기죠. Veneer는 이런 원거리 짬푸도 가능하게 해주는 다리 역할도 한답니다. Linker는 최대한 Jump를 가깝게 만들고 싶겠지만, 가끔 scatter loading을 이용해서 프로그래머가 불리는 함수를 멀리~ 떨어 뜨려 놓았을 경우 Linker가 이런 Veneer를 만들어 넣어 원거리 짬푸가 가능하도록 해준 답니다.

그러다 보니, 디버깅 하다 보면 분명 어떤 함수를 호출했는데 Step in으로 따라 들어가다 보면 몇 다리 이상한 곳을 걸쳐 들어가는 경우가 생기죠. 그게 바로 이~ 경웁니다.

 

Linked at 임베디드 시스템 개발자 되기 .. at 2009/07/05 22:10

... 와 Reverse Engineering ⓒ ARM Thumb mode와 S 접미사 ⓓ ARM과 Thumb 사이의 Veneer (베니아) ⓔ Inline Assembly와 INTLOCK()구현 ... more

친절한 임베디드 시스템 개발자 되기 강좌 글 전체 리스트 (링크) -



댓글





친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -