Symbol을 읽을 때, 동작하는 Binary가 compile된 주소에 잘 load되어 있다면, LDR instruction을 통해서 Symbol의 위치를 얻어 올 수 있다. 하지만 Binary가 해당 주소에 적재가 안되었을 때에는 LDR instruction을 통해서 symbol의 주소에 접근하면 잘못 된 값을 가져올 수도 있다.

따라서, ARM은 adrp instruction을 통해 PC-relative addressing mechanism을 제공해준다. 

 

예를 들어 foo라는 심볼의 주소를 register x0에 넣으려면 아래와 같이 foo[63:12]를 adrp 명령어를 통해 pc기준 상대 주소로 가져온 후 foo[11:0]의 주소는 #:lo12:foo를 통해서 얻어 올 수 있다:

adrp x0, :pg_hi21:foo
add  x0, x0, #:lo12:foo

 

만약 foo라는 심볼의 주소를 얻어 오기 보다는 해당 심볼에 바로 접근하여 심볼로 부터 값을 읽어 오려고 한다면 아래와 같이 ldr 명령어를 통해 얻어 올 수 있다.

adrp x0, :pg_hi21:foo
ldr  x0, [x0, #:lo12:foo]


Referrence: https://sourceware.org/binutils/docs/as/AArch64_002dRelocations.html

'전공공부 > ARM' 카테고리의 다른 글

[ARM] Alignment가 무엇인가?!  (0) 2019.02.17
[링커스크립트] 지시어 ENTRY  (0) 2019.02.12
GICv3 LPI, ITS  (0) 2018.07.18
interrupt status - level sensitive  (0) 2018.07.17
ARMv8 HPPIR/IAR, AHPPIR/AIAR  (0) 2018.03.27

이 글에서는 리눅스 헤더로 부터 넘어온 첫 번째 함수 stext에 대해서 알아볼 것이다.


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16 

ENTRY(stext)
    bl  preserve_boot_args
    bl  el2_setup           // Drop to EL1, w0=cpu_boot_mode
    adrp    x23, __PHYS_OFFSET
    and x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0
    bl  set_cpu_boot_mode_flag
    bl  __create_page_tables
    /*
     * The following calls CPU setup code, see arch/arm64/mm/proc.S for
     * details.
     * On return, the CPU will be ready for the MMU to be turned on and
     * the TCR will have been set.
     */
    bl  __cpu_setup         // initialise processor
    b   __primary_switch
ENDPROC(stext)


stext 함수는 여기서 ENTRY라는 마치 함수와 같은 것으로 둘려져 있는데, 여기서 ENTRY는 링커스크립트의 것과는 다르게 리눅스 내에서 매크로로 정의되어 있는 function-like macro일 뿐이다.


2번 째 줄의 preserve_boot_args에 대해서 간략하게 알아보자면, 이전 글의 마지막에서 언급한 적이 있는데, 리눅스는 시작 할 때 몇 개의 값을 전달 받고 시작하게 된다고 언급한 적이 있다. 이 값들이 지금 당장은 쓰이지 않지만 나중에 쓰이기 위해 따로 저장하기 위함이라 볼 수 있다.


1

2

3

4

5

6

7

8

9

10

11

12

13

 preserve_boot_args:
    mov x21, x0             // x21=FDT

    adr_l   x0, boot_args           // record the contents of
    stp x21, x1, [x0]           // x0 .. x3 at kernel entry
    stp x2, x3, [x0, #16]

    dmb sy              // needed before dc ivac with
                        // MMU off

    mov x1, #0x20           // 4 x 8 bytes
    b   __inval_dcache_area     // tail call
ENDPROC(preserve_boot_args)



linux/arch/arm64/kernel/setup.c 에 위치하고 있는 아래의 boot_args에 저장하게 되는 꼴이 된다.

u64 __cacheline_aligned boot_args[4];



사실, 이 글은 딱히 ARM architecture에 국한 된 것은 아니다.


"Alignment가 무엇인가" 대한 글인데.. 살짝 요약하기 전에 바로 본론으로 들어가보도록 한다.



컴퓨터라는 놈은 특히 ARM architecture에서는 무언가를 실행하기 위해서, CPU 바로 옆에 있는 레지스터와 메모리 사이에 읽고 쓰는

연산이 굉장히 많다. 이때 주의깊게 봐야 할 점은 메모리에서, 어떤 주소에서 얼마 만큼 읽어 올 것인가가 주된 내용이다.


아래 그림에서 각 메모리 주소들은 주소를 나타내고 각 주소마다 1byte 만큼을 의미한다.



각각의 예시를 통해서 개발자가 생각하는 컴퓨터의 예상 행동과 실제 컴퓨터의 행동을 비교해보도록 해보자
먼저, 개발자가 생각하는 컴퓨터의 예상 행동은

  • READ 주소0 부터 1byte: 아래 색칠 되어있는 0 부분만 읽어오는 것

  • READ 주소3 부터 2byte: 아래 색칠되어있는 3 그리고 4를 읽어오는 것
  • READ 주소3 부터 4byte: 아래 색칠되어있는 3, 4, 5 그리고 6를 읽어오는 것


하지만, 실제 컴퓨터의 동작은 프로그래머가 생각하는 방식으로 동작하지 않고 읽거나 쓸 때 Chunk 단위로 동작하는데 이 단위는 CPU에 따라 다르다. 예를 들어 32bit architecutre에서는 32bit chunk 단위로 움직이고, 64bit architecture에서는 64bit chunk 단위로 움직이게 된다. 즉,

  • READ 주소0 부터 1byte: 0 번째 byte를 읽기 위해서 0,1,2 그리고 3번째 byte를 모두 읽고, 그 중 0번 째 byte 값만 다시 OR 연산을 통해
                                             해당 0 번째 byte만을 결과로 갖는다.
  • READ 주소3 부터 2byte: 3 번째 부터 2 byte를 읽기 위해서 0,1,2,3 그리고 4,5,6,7 번째 byte를 모두 읽고, 그 중 3번 째, 4번째 byte 값만 다시
                                            OR 연산을 통해 결과로 갖는다
  • READ 주소3 부터 4byte: 3 번째 부터 4 byte를 읽기 위해서 0,1,2,3 그리고 4,5,6,7 번째 byte를 모두 읽고, 그 중 3,4,5,6번 째 byte 값만 다시 OR
                                             연산을 통해 결과로 갖는다.

 이처럼, 프로그래머가 읽고 싶은 번째부터 몇 개의 byte만을 읽기 위해서 실제로는 chunk 단위로 읽어 들여야 하다 보니 생각치 못한 오버헤드가 생긴다. 그래서 CPU가 자체적으로 이렇게 오버헤드가 생기지 않게 하기 위해서, Alignment check를 통해서 자체적으로 오버헤드를 줄일 수 있게 해 놓았다.(읽고자 하는 단위와, 주소를 통해서 Alignment를 체크한다.)




 Memory Access

 Alignment (8bit)

 Alignment (16bit)

 Alignment (32bit)

 Alignment (64bit)

 0x0000_0000

 Aligned

 Aligned

 Aligned

 Aligned

 0x0000_0001

 Aligned

 Non-Aligned

 Non-Aligned

 Non-Aligned

 0x0000_0002

 Aligned

 Aligned

 Non-Aligned

 Non-Aligned

 0x0000_0003

 Aligned

 Non-Aligned

 Non-Aligned

 Non-Aligned

 0x0000_0004

 Aligned

 Aligned

 Aligned

 Non-Aligned

 0x0000_0005

 Aligned

 Non-Aligned

 Non-Aligned

 Non-Aligned

 0x0000_0006

 Aligned

 Aligned

 Non-Aligned

 Non-Aligned

 0x0000_0007

 Aligned

 Non-Aligned

 Non-Aligned

Non-Aligned

 0x0000_0008

 Aligned

 Aligned

 Aligned

 Aligned




위에 테이블과 같이 해당 메모리는 접근 할 때, 위에 표를 참조하여 Alignment를 체크하고 올바르지 않은 접근을 하게 되면 Alignment Fault를 발생시킨다.



















'전공공부 > ARM' 카테고리의 다른 글

ARM Relocations (adrp)  (0) 2020.10.18
[링커스크립트] 지시어 ENTRY  (0) 2019.02.12
GICv3 LPI, ITS  (0) 2018.07.18
interrupt status - level sensitive  (0) 2018.07.17
ARMv8 HPPIR/IAR, AHPPIR/AIAR  (0) 2018.03.27

+ Recent posts