본문 바로가기

Programming/ARM

compile 된 binary 가 thumb / thumb-2 인지 check 하기

오늘은 thumb-2 code 가 절 고민하게 만들었습니다.

아시다 시피 ARM 은 기본적으로 두가지 모드(arm code , thumb code) 를 지원합니다.
arm 은 32bit instruction 이고 thumb 는 16 bit instruction 이지요.

헌데 thumb-2 와 thumbEE 라는 32 bit instruction 이 나와서 instruction 의 길이만으로 구별하기가 참 애매해 졌습니다.

단순하게 thumb 만으로만 build 된 경우 objdump 로 build 된 영역을 interpret 해 보면 thumb 인지 금방 알 수 있습니다.
instruction 이 16bit 로 나오거든요..

thumb mode 로 빌드하려면 컴파일 시 다음 옵션을 추가하면 됩니다.

 -mthumb -mthumb-interwork

< thumb code 로 컴파일된 binary >
00008378 <NeonTest>:
    8378: b580       push {r7, lr}
    837a: b086       sub sp, #24
    837c: af00       add r7, sp, #0
    837e: 1c3b       adds r3, r7, #0
    8380: 330c       adds r3, #12
    8382: 6018       str r0, [r3, #0]
    8384: 1c3b       adds r3, r7, #0

< arm code 로 컨파일된 binary >
00008378 <NeonTest>:
    8378: e52db004  push {fp}  ; (str fp, [sp, #-4]!)
    837c: e28db000  add fp, sp, #0 ; 0x0
    8380: e24dd01c  sub sp, sp, #28 ; 0x1c
    8384: e50b0010  str r0, [fp, #-16]
    8388: e50b1014  str r1, [fp, #-20]
    838c: e50b2018  str r2, [fp, #-24]
    8390: e3a03000  mov r3, #0 ; 0x0
    8394: e50b3008  str r3, [fp, #-8]
    8398: ea000016  b 83f8 <NeonTest+0x80>

동일한 C 코드를 옵션만 바꾸어서 빌드한 경우입니다.

하지만 thumb-2 의 경우는 이렇게 판단하기가 쉽지 않습니다.

구글링을 한참 하다가 아래 링키에서 해답을 찾을 수 있었습니다.

https://wiki.ubuntu.com/ARM/Thumb2PortingHowto

풀이해서 말하자면
thumb-2 로 컴파일 된 녀석은 buttom bit 가 set 되어 있다고 합니다. 한마디로 홀수 주소를 가지는 거지요.
하지만 objdump 로 interpret 을 하면 buttom bit 를 빼고 읽는 다고 합니다.
위에 NenoTest 함수가 Thumb code 로 빌드되었는데도 불구하고 짝수 주소를 가지는 이유 입니다.
readelf 로 symbol 정보를 보면 thumb 로 빌드한 경우 홀수 주소를 가지는 것을 볼 수 있습니다.

> arm-none-linux-gnueabi-readelf -s buildtest-thumbR


    95: 000105d8     0 NOTYPE  GLOBAL DEFAULT  ABS _bss_end__
    96: 000105d8     0 NOTYPE  GLOBAL DEFAULT  ABS _end
    97: 00008379   132 FUNC    GLOBAL DEFAULT   12 NeonTest
    98: 000105d4     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
    99: 000084a8     0 NOTYPE  GLOBAL DEFAULT  ABS __exidx_start
   100: 000083fd    50 FUNC    GLOBAL DEFAULT   12 main
   101: 0000828c     0 FUNC    GLOBAL DEFAULT   10 _init

직접 compile 한 NeonTest 와 main 함수가 홀수 주소를 가지는 것을 볼 수 있습니다.

다만 thumb-2 code 의 경우 32bit arm code 와 혼용해서 사용하게 되는데
이 방법으로는 symbol 로 매칭이 되어있을 경우만 판단이 가능합니다.
arm code 사이에 끼어있는 thumb-2 code 는 구별할 수 없는것이지요.

그 방법에 대해서는 알게 되는데로 posting 하도록 하겠습니다.

- thumb instruction quick reference card
http://infocenter.arm.com/help/topic/com.arm.doc.qrc0006d/QRC0006_UAL16.pdf

- thumb-2 instruction quick reference card
http://infocenter.arm.com/help/topic/com.arm.doc.qrc0001l/QRC0001_UAL.pdf

- arm instruction quick reference card
http://www.simplemachines.it/doc/QRC0001H_rvct_v2.1_arm.pdf


또 하나 재미있는것..

다음과 같이 파일을 하나 만듭니다.


> cat tt
.aaa:
mov r1, r2

목적파일을 만듭니다.

arm-none-linux-gnueabi-as tt -o aa.out

이것을 objdump 로 보면 다음과 같습니다.


arm-none-linux-gnueabi-objdump -d aa.out
aa.out:     file format elf32-littlearm
Disassembly of section .text:
00000000 <.aaa>:
   0:   e1a01002        mov     r1, r2

다시 -mthumb -mthumb-interwork 옵션으로 컴파일 합니다.

arm-none-linux-gnueabi-as tt -o aa.out -mthumb -mthumb-interwork

이것을 objdump 로 보면 compile 가 mov 에 대응하는 adds 의 thumb 로 변환해서 compile 한것을 볼 수 있습니다.


arm-none-linux-gnueabi-objdump -d aa.out              
aa.out:     file format elf32-littlearm

Disassembly of section .text:

00000000 <.aaa>:
   0:   1c11            adds    r1, r2, #0

추가로 readelf 로 읽을때 다음과 같이 출력되는데 $t 는 thumb를 $a 는 arm code 를 의미합니다.

arm-none-linux-gnueabi-readelf -s aa.out         
Symbol table '.symtab' contains 7 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 SECTION LOCAL  DEFAULT    1
     2: 00000000     0 SECTION LOCAL  DEFAULT    2
     3: 00000000     0 SECTION LOCAL  DEFAULT    3
     4: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 .aaa
     5: 00000000     0 NOTYPE  LOCAL  DEFAULT    1 $t
     6: 00000000     0 SECTION LOCAL  DEFAULT    4