[컴퓨터구조] Ch7-5. CA_Interworking

김규원·2024년 6월 8일

24-1: 컴퓨터구조(完)

목록 보기
12/15
post-thumbnail

▶ Interworking Basics

The need for interworking(interworking의 필요성)

  • Thumb의 코드 밀도와 좁은 메모리에서의 성능 덕분에 많은 시스템에서 C 코드의 대부분에 이상적임.
  • 하지만 대부분의 응용 프로그램에서는 ARM 상태와 Thumb 상태 간에 전환이 필요함.
  • ARM 코드는 넓은(32비트) 메모리에서 더 나은 성능을 제공.
  • 따라서 속도에 민감한 응용 프로그램 부분에서는 ARM 코드를 사용해야 함.
  • 일부 작업은 ARM 명령어로만 수행 가능
    ex1. CPSR 접근
    (인터럽트 활성화/비활성화 및 모드 변경)
    ex2. 코프로세서 접근
    ex3. DSP 수학 명령어
  • 예외 처리
  • 예외 발생시, ARM 상태로 자동 진입
  • 그러나 시스템 사양에 따라 주요 핸들러에서 Thumb 코드를 사용해야 할 수도 있음.
  • 간단한 독립형 Thumb 프로그램도 상태 변경 및 Thumb 루틴 호출을 위해 ARM 어셈블러 헤더가 필요함(시작 시 프로세서는 ARM 상태에 있음).

Interworking Instructions

  • 상호작용은 BX(Branch eXchange)명령어를 사용하여 달성
  1. Thumb 상태에서 BX Rn
  2. Arm 상태에서 BX <condition> Rn
    : 여기서 Rn은 r0~r15까지의 모든 레지스터
  • Rn을 프로그램 카운터로 복사하여 4GB 주소 공간의 절대 주소로 분기(Branch)
  • Rn의 비트 0은 변경할 상태를 지정

Branch Exchange Example

; ARM 상태에서 시작합니다
ARM
:
ADR r0, GoThumb:OR:1 ; 분기 대상 주소를 생성하고
                      ; 비트 0을 설정하여 Thumb 상태로 도착
BX r0 ; Branch exchange를 통해 Thumb 상태로 전환
THUMB ; 이후 코드를 Thumb로 어셈블리합니다
GoThumb
:
ADR r5, GoARM ; 분기 대상을 워드 정렬된 주소로 생성하여
               ; 비트 0이 클리어된 상태로 만듭니다
BX r5 ; Branch exchange를 통해 ARM 상태로 다시 전환
ARM ; 이후 코드를 ARM으로 어셈블리합니다
GoARM
:

상세 설명

  • ADR r0, GoThumb:OR:1:

    • ADR 명령어를 사용하여 GoThumb 레이블의 주소를 r0 레지스터에 저장합니다.
    • :OR:1는 주소의 비트 0을 설정하여 Thumb 상태로의 전환을 나타냅니다.
  • BX r0:

    • r0 레지스터에 저장된 주소로 분기합니다.
    • 비트 0이 설정되어 있으므로, Thumb 상태로 전환됩니다.
  • THUMB:

    • 이후 코드는 Thumb 상태로 어셈블리됩니다.
  • ADR r5, GoARM:

    • ADR 명령어를 사용하여 GoARM 레이블의 주소를 r5 레지스터에 저장합니다.
    • 워드 정렬된 주소이므로, 비트 0이 클리어되어 있습니다.
  • BX r5:

    • r5 레지스터에 저장된 주소로 분기합니다.
    • 비트 0이 클리어되어 있으므로, ARM 상태로 전환됩니다.
  • ARM:

    • 이후 코드는 ARM 상태로 어셈블리됩니다.

▶ Interworking Subroutines and Veneers

Non-interworking subroutines

서브루틴 호출

  1. 복귀 주소를 LR(링크 레지스터)에 저장
  2. 필요한 서브루틴의 주소로 분기
    : 이러한 작업은 일반적으로 BL이라는 하나의 명령어로 실행
    : 복귀는 LR에서 PC로 복원하여 구현됨.

Interwokring Subroutines

  • 한 상태로 컴파일된 함수가 다른 상태의 함수를 호출할 수 있는 방법이 필요
    : 전통적인 BL, MOV PC/LR, 은 호출 및 복귀 시 상태를 변경하지 않음
    : 컴파일러 스위치 --apcs /interwork를 지정
    : --apcs /interwork 스위치는 ARM과 Thumb 코드가 서로 호출할 수 있도록 설정
  • 이를 통해 컴파일러가 복귀 시, MOV PC/LR 대신 BX LR을 사용하도록 강제
  • BL이 Thumb 상태에서 복귀 주소의 최하위 비트를 설정하기 때문에 BX LR은 항상 올바른 상태로 복귀
  • 링커는 상태를 변경하는 작은 코드 조각인 Veneer(베니어)를 자동으로 생성

BL(Branch with Link) 명령어가 Thumb 상태에서 호출되면 복귀 주소의 최하위 비트를 0로 설정하여 ㅁARM 상태로 전환해야 함을 나타냄. 즉, 최하위 비트는 1일 때 Thumb 상태, 0일 때 ARM 상태

시나리오

MyFunction:
    ; 함수의 본문
    ...
    BL AnotherFunction ; AnotherFunction 호출
    ...
AnotherFunction:
    ; 다른 함수의 본문
    ...
    BX LR ; 복귀
  • 위의 코드에서 BL 명령은 AnotherFunction으로의 호출을 수행하고, LR 레지스터에 MyFunction 다음에 실행할 주소를 저장합니다. 그리고 AnotherFunction은 실행을 완료하고 BX LR을 사용하여 저장된 주소로 복귀합니다. 이렇게 함으로써 MyFunction의 실행을 계속할 수 있습니다.

BL Vs. BX

BL 명령어는 "Branch with Link"의 약어로, 분기할 주소를 저장하기 위해 리턴 주소를 링크 레지스터(LR)에 저장하는 동시에 해당 주소로 분기합니다. 이렇게 하면 해당 서브루틴이 실행을 마치고 복귀할 때 사용할 리턴 주소를 기록해 둘 수 있습니다. BX 명령어는 "Branch and Exchange"의 약어로, 레지스터의 값을 프로그램 카운터(PC)에 복사하여 해당 주소로 분기합니다. 따라서 BL은 서브루틴 호출에 사용되며, BX는 분기 명령으로 사용됩니다.

Building for interworking

  • --apcs /interwork 옵션을 사용하여 다른 상태에서 호출될 수 있는 모든 코드를 컴파일/어셈블
  1. armcc --arm --apcs /interwork arm_code.c -o arm_code.o
    : ARM 아키텍처에서 호출될 수 있는 C 코드를 ARM 아키텍처로 컴파일하고, --apcs /interwork 옵션을 사용하여 상호 운용성(interworking)을 활성화
    : 그 결과로 생성된 객체 파일은 arm_code.o입니다.

  2. armcc --thumb --apcs /interwork thumb_code.c -o thumb_code.o
    : Thumb 아키텍처에서 호출될 수 있는 C 코드를 Thumb 아키텍처로 컴파일하고, --apcs /interwork 옵션을 사용하여 상호 운용성을 활성화
    : 그 결과로 생성된 객체 파일은 thumb_code.o입니다.

  3. armasm --apcs /interwork asm.s -o asm.o
    : 다른 상태에서 호출될 수 있는 어셈블리 코드를 어셈블
    --apcs /interwork 옵션은 상호 운용성을 활성화
    : 그 결과로 생성된 객체 파일은 asm.o입니다.

이렇게 생성된 객체 파일들은 링킹 단계에서 하나의 실행 파일로 결합 가능

  • --apcs /interwork를 사용하여 armasm를 사용하는 경우 코드를 어셈블하는 방식이 변경되지 않음
  • 이는 단순히 링커에게 코드가 상호 작동 가능하다고 알려줌.
  • 그러나 코드가 올바른 리턴 명령어(BX LR)를 사용하도록 해야함.
  • 코드를 ARM 및 Thumb 간에 어떻게 분할할지는 개발자가 결정해야 함.
  • 도구가 자동으로 수행하지는 않음.

Switching state within a module

  • #pragma arm
    : 해당 소스 파일에서 ARM 코드 생성을 강제
  • #pragma thumb
    : 해당 소스 파일에서 Thumb 코드 생성을 강제
  • 이러한 #pragma를 사용하여 소스 파일 내에서 ARM 또는 Thumb 코드 생성을 강제할 수 있음.
  • 따라서 ARM 및 Thumb 코드를 별도의 파일로 분리할 필요 x.
  • 이러한 #pragma를 포함하는 모듈은 --apcs /interwork를 사용하여 컴파일되어야 함.
  • 이렇게 함으로써 다른 상태에서 호출할 수 있는 함수들이 성공적으로 호출될 수 있음.
// armcc --apcs /interwork
void ARMfunction(void)
{
:
}
#pragma thumb
void Thumbfunction(void)
{
:
}
#pragma arm
void ARMfunction2(void)
{
:
}
  • ARMfunction
    : ARM 함수로 정의되어 있음
  • Thumbfunction
    : Thumb 함수로 정의되어 있음
  • ARMfunction2
    : 다시 ARM 함수로 정의되어 있음

Interworking with Arch v4T

  • 반환 시퀀스는 반환 명령어로써 BX를 사용하도록 수정됨.
  • Thumb 상태에서는 LR로 POP할 수 없으므로, 복귀 주소를 저장하기 위해 임시 레지스터인 r3가 사용됨.
  • 함수에 진입할 때, 복귀 주소와 손상된 레지스터들은 일반적인 방식으로 스택에 저장됨.

함수 내 반환 과정 예시


1. LDMFD sp!, {r4-r11, lr}
: 스택 포인터(sp)로부터 레지스터 r4부터 r11까지 그리고 Link Register(LR)까지 값을 팝(pop)하여 해당 레지스터들을 복원
2. POP {r4-r7}
: 스택으로부터 레지스터 r4부터 r7까지 값을 팝하여 해당 레지스터들을 복원
3. POP {r3}
: 스택으로부터 레지스터 r3의 값을 팝하여 복원
: POP {r3}를 사용하는 이유는 Thumb 모드에서는 직접적으로 Link Register(LR)에 POP을 수행할 수 없기 때문
: 대신, r3와 같은 임시 레지스터를 사용하여 반환 주소를 저장하고 나중에 이를 사용하여 반환
: 다시 말해, r3는 LR의 역할을 대신
4. BX r3: 레지스터 r3에 저장된 주소로 분기하여 함수를 반환

What to build for Interworking?

  • 모든 소스 코드를 상호 동작하도록 빌드하는 것이 강력히 권장
  • 렇지 않으면 반드시 반대 상태에서 호출될 수 있는 모든 코드가 상호 동작하도록 컴파일되었는지 확인해야 함.
  • 링커는 대부분의 문제에 대해 경고를 표시하지만, 절대 주소를 사용할 때 주의해야 함.
  • 링커는 호환성이 없는 것을 감지하면 오류를 반환
    Error: L6239E: THUMB 코드인 thumbcode.o(.text)에서 armcode.o의 armfunc와 같은 비-상호 동작하는 ARM 심볼을 호출할 수 없습니다.
  • 간단히 컴파일러/어셈블러 구성을 수정하여 다시 빌드하면 됨(e.g. --apcs /interwork 추가).

Linker Generated Venners

Venners

  • 분기가 상태 변경을 유발하는 경우
  • 분기가 분기 명령의 범위를 벗어나는 대상을 포함하는 경우
    → 위 경우 자동으로 링커에 의해 삽입되는 작은 코드 조각
  • Veneer는 원래 분기의 중간 대상이 됨.
  • 그런 다음 Veneer는 대상 주소로 분기
  • 다음은 ARM-Thumb 상호 동작 Veneer의 예시를 보여줌.
$Ven$AT$L$thumbfunc
LDR r12, [pc, #0]
BX r12
DCD 0x2200813
; address of thumbfunc + 1
  • $Ven$AT$L$thumbfunc: Veneer의 이름을 나타내는 특별한 토큰
  • LDR r12, [pc, #0]: 현재 프로그램 카운터(PC)의 위치에서 4바이트(워드) 앞에 저장된 주소를 읽어 레지스터 r12에 로드. 이 주소는 Thumb 함수인 thumbfunc의 시작 주소.
  • BX r12: r12에 저장된 주소로 분기. 이는 Thumb 함수로의 분기를 실행함.
  • DCD 0x2200813: Thumb 함수인 thumbfunc의 시작 주소에서 1을 더한 값을 나타내는 데이터 워드. Thumb 함수로 분기하기 위해 사용.

    Thumb 함수는 주소가 항상 홀수여야 합니다. 따라서 Thumb 함수로 분기할 때는 해당 주소의 최하위 비트가 1이어야 합니다. 그러나 ARM 코드는 주소가 짝수이므로 Thumb 함수의 주소로 직접 분기할 수 없습니다. 따라서 Thumb 함수로의 분기를 위해서는 해당 주소에서 1을 더한 값을 사용해야 합니다. 이렇게 하면 분기 목적지의 주소의 최하위 비트가 항상 1이 되어 Thumb 상태로 분기할 수 있게 됩니다. 따라서 Thumb 함수로의 분기를 위해서는 이러한 조치가 필요합니다.

Minimizing the Number of Veneers

  • 링커는 이전 호출을 위해 생성된 베니어를 다음 호출에 재사용 가능
  • 단 재사용된 베니어는 양쪽 위치에서 범위 내에 있어야 함.
    EX. 같은 함수를 호출하는 두 곳에서 생성된 베니어
    : 해당 함수의 범위 내에서 모두 사용 가능

—info veneers 옵션을 사용하여 추가된 베니어에 대한 요약

  • 'Thumb_to_ARM.o(.text)'에서 '_printf'를 호출하는 경우에는 AT 베니어(12바이트)가 추가
  • 'thumbmain.o(.text)'에서 'arm_function'을 호출하는 경우에는 TA 베니어(12바이트)가 추가
  • AT(Arm에서 Thumb), TA(Thumb에서 Arm)

  • 베니어 수를 줄이기 위해서
    : 기능적으로 관련된 코드를 메모리 맵 상에서 함께 유지해야 함(베니어 공유 ↑)
    : 함수 호출 트리에서 상태 변경의 수 최소화
  • 함수의 ARM 및 Thumb 버전을 모두 빌드 가능
    : ARM 및 Thumb 버전은 동일한 이름을 가짐
    : 링커에 의해 올바른 버전으로 해결(상태 변경 없음)
    : 즉, 함수가 호출될 때 현재 상태를 유지하면서 호출된 상태로 작동

▶ Architecture v5TE Extensions

Architecture v5TE Interworking

  • v5TE는 상호 운용성을 추가로 지원
  • 이로써 대부분 상호 운용성 베니어가 필요 없어짐.
  • 또한 상호 운용성을 위해 생성된 코드(베니어가 있는)는 여전히 호환
  • BLX <offset>
    : 오프셋을 사용한 분기 명령
  • BLX <register>
    : 레지스터를 사용한 분기 명령
  • PC로의 로드에 대한 수정된 동작

BLX <offset>

  • 이는 사실상 표준 BL 명령어
  • 항상 상태를 변경
  • ARM에서는 Thumb 코드로의 서브루틴 호출이며, 32MB의 분기 범위를 가지며, 복귀 주소가 LR에 저장
  • 서브루틴에서는 BX LR을 사용하여 복귀
  • Thumb에서는 ARM 코드로의 서브루틴 호출로, 두 개의 16비트 명령으로 변환
  • 오프셋 범위는 표준 Thumb BL 명령어와 일치
  • 범위는 약 +/- 4MB
  • 복귀 주소(LSB 설정)가 LR에 저장
  • 컴파일러는 여전히 BL을 사용할 것이며, 링커는 적절한 경우 BL을 BLX로 수정

BL Vs. BLX

  • BL(Branch with Link) 명령어는 서브루틴으로의 분기를 수행하고, 분기하기 전의 주소를 복귀 주소로서 LR(Link Register)에 저장합니다. 이 명령어는 ARM 상태에서 서브루틴으로의 분기를 지원합니다.
  • BLX(Branch with Link and Exchange) 명령어는 BL 명령어와 유사하지만, 분기 지점이 Thumb 상태로 변경됩니다. 따라서 BLX는 ARM 상태와 Thumb 상태 간의 서브루틴 호출을 모두 지원합니다. 이러한 차이로 인해 BLX는 더 넓은 범위의 서브루틴 호출을 지원하며, Thumb 상태에서 ARM 상태로의 분기도 가능합니다.
  • 컴파일러는 BL 명령어를 사용하여 서브루틴 호출을 생성하며, 링커는 필요한 경우 BL을 BLX로 변경하여 ARM 및 Thumb 상태 간의 상호운용성을 보장합니다.

  • BLX (Branch with Link and Exchange) 명령어는 기본적으로는 표준 BX 명령어와 동일하며, 추가적으로 반환 주소를 LR(Link Register)에 저장
  • 이 명령어는 ARM 버전과 Thumb 버전이 모두 존재
  • 함수 포인터를 사용하는 데 유용
  • ARM 버전의 BLX는 조건부이며, 반환 주소를 LR에 저장한 후 분기
  • Thumb 버전의 BLX는 단일 16비트 명령어로 표현, LR에 반환 주소를 저장하면서 분기
  • 함수 포인터를 통해 서브루틴을 호출하고 반환 가능

Loads to the Program Counter

  • 아키텍처 5TE에서는 PC(Program Counter)로의 로드 명령어가 자동으로 T 비트를 설정 가능
  • 이는 로드된 값의 0번째 비트를 사용하여 T 비트를 설정
  • 이를 통해 필요한 경우 상태를 변경하고 BX LR 시퀀스를 사용하지 않고 서브루틴에서 복귀 가능
  • 이러한 기능은 저장된 레지스터를 복원하거나 서브루틴에서 복귀할 때 사용
  • 또한 ARM 점프 테이블에도 사용 가능


LDR PC, [Rn,...]
: T 비트를 설정
: Thumb에서는 이와 같은 명령어가 없으며 컴파일러는 대신에 BX를 사용

  • ARM 및 Thumb 모두에서 프로그램 카운터(PC)를 로드하는 데 사용
  • ARM에서는 LDMFD 명령어를 사용하여 스택 포인터(SP)를 감소시키고, 스택에서 여러 레지스터를 로드한 다음, 마지막으로 PC를 로드. 이때 마지막 레지스터에는 PC가 포함되어야 함.
  • Thumb에서는 POP 명령어를 사용하여 스택에서 여러 레지스터를 로드하고, 마지막으로 PC를 로드. 이때 마지막 레지스터에는 PC가 포함되어야 함.
  • 이러한 명령어들은 함수의 끝에서 저장된 레지스터를 복원하고 함수에서 복귀할 때 사용

Interworking with Arch v5TE

아키텍처 v4T와 v5TE에서의 리턴 코드 비교

  • ARM에서는 한 개의 명령어를, Thumb에서는 두 개의 명령어를 절약

▶ Appendix

Special Cases and Library Use

  • 함수 포인터의 경우 컴파일러는 인터워킹 안전성을 보장하기 위해 호출 함수를 생성
  • Thumb에서는 MOV lr, pc를 사용할 수 없으므로 도우미 함수가 사용
  • 정적 점프 테이블(Static jump table)은 유사한 메커니즘을 사용가능
  • 이미지에 Thumb 코드가 있는 경우 링커는 Thumb 라이브러리를 선택
  • 전형적인 시스템의 대부분 코드는 코드 크기를 줄이기 위해 Thumb에서 주로 사용되며, 성능이 중요한 부분에서만 ARM이 사용

Types of Veneers

  • 긴(long) 베니어 (분기 범위를 증가시키고 선택적으로 상태 변경)
  • 짧은(short) 베니어 (상태 변경만)
  • 인라인(inline) 베니어 (state change only, 베니어 처리되는 함수의 시작 부분에 추가)
profile
행복한 하루 보내세요

0개의 댓글