GNU binutils 문서 · ELF 개념 · bare-metal 예와 Linux loader 구분

어셈블·링크 때 정하는 값과 실행할 때 바뀌는 값

지시어, relocation, ELF의 적재 주소와 실행 주소를 나누어 부팅 코드를 읽습니다.

같은 파일 안에도 처리 시점이 다릅니다

표현처리하는 쪽결과
#if defined(CONFIG_...)전처리기해당 설정에서 assembler에 넘길 텍스트를 선택합니다. 실행 중 조건문이 아닙니다.
.quad 0assembler64비트 폭의 0 데이터를 내보냅니다. CPU의 어느 레지스터를 0으로 만드는 명령이 아닙니다.
.quad .Ltable - .Lheadassembler와 필요할 경우 linker두 label 사이의 차이를 데이터로 표현합니다. 그 자리에서 CPU가 뺄셈을 실행하는 것은 아닙니다.
add x0, x1, x2CPU실행 시 x1과 x2의 값을 더해 x0에 씁니다. 파일에 적힌 symbol offset과는 다른 단계입니다.

“어셈블·링크 단계에서 채울 데이터”라는 설명에는 중요한 구분이 들어 있습니다. 다만 .quad 0처럼 assembler가 즉시 결정하는 상수까지 반드시 linker가 나중에 계산한다고 쓰면 부정확합니다. label 차이도 assembler가 이미 알 수 있으면 그때 확정합니다. 아직 결정할 수 없는 값은 대상 형식이 지원하는 relocation과 linker 처리 규칙을 확인해야 합니다. .quad의 의미 assembler의 식 relocation 항목

VMA는 사용할 주소, LMA는 적재할 주소입니다

Flash에 이미지를 두고 RAM에서 데이터를 사용하는 작은 firmware를 생각해 보겠습니다. 초기값 있는 전역 변수는 이미지 안에 원래 값이 있어야 하고, 실행 중에는 RAM에 놓여야 합니다. linker script는 그 두 위치를 표현할 수 있지만, linker가 보드에서 복사 명령을 실행해 주는 것은 아닙니다.

영역 예이미지·적재 단계CPU가 실행할 때
.textFlash에 명령 바이트가 있습니다.XIP 구성이라면 같은 Flash 주소에서 명령을 실행합니다.
.data초기값 바이트가 Flash의 LMA 위치에 있습니다.startup 코드가 RAM의 VMA 위치로 복사한 뒤 변수를 사용합니다.
.bss보통 ELF의 NOBITS 영역으로 크기·주소를 나타냅니다.bare-metal startup 코드가 해당 RAM을 0으로 만듭니다.

Linux ELF 프로세스는 loader와 메모리 매핑 규칙으로 초기 상태를 마련합니다. 모든 ELF가 “사용자 startup 코드에서 .data를 복사하고 .bss를 직접 지운다”는 것은 아닙니다. 지금 설명하는 Flash→RAM 예와 구분해야 합니다. VMA와 LMA ELF program header와 적재

숫자 예제로 파일의 값과 RAM의 값을 봅니다

초기값 7을 가진 전역 변수의 위치

1. 링크 결과

설명용으로 .data의 LMA를 0x08004000, VMA를 0x20000000이라고 정합니다. 변수의 초기값 7을 표현한 바이트가 이미지에 들어갑니다. 이 주소들은 가상의 보드 예이며 실제 장치의 고정 주소가 아닙니다.

2. reset 직후

Flash에는 7의 초기값이 있어도 RAM 0x20000000의 내용은 아직 그 값이라고 보장할 수 없습니다. CPU가 reset됐다는 이유만으로 모든 RAM이 0이 되거나 .data 초기값을 갖지는 않습니다.

3. startup 복사 후

startup 코드가 정해진 길이만큼 LMA에서 VMA로 복사합니다. 이제 해당 RAM 변수의 값이 7입니다. Flash 원본을 지우는 작업이 아니며 PC가 그 변수 주소로 분기하는 것도 아닙니다.

4. 일반 코드 실행

프로그램이 변수에 9를 쓰면 RAM의 값이 9로 바뀝니다. 다시 부팅할 때 startup이 초기값을 복사하는 구성이라면 또 7에서 시작합니다.

단계는 이미지 생성·부팅·실행의 시간 순서입니다. 주소의 크고 작음이나 stack 증가 방향을 나타내는 그림이 아닙니다.

linker script의 ENTRY(symbol)은 출력의 entry point를 지정합니다. 실제 CPU가 그 주소에 도달하도록 만드는 일은 boot ROM·부트로더·loader의 역할입니다. ENTRY를 적었다고 전원이 켜질 때 하드웨어 reset vector가 자동으로 바뀌지는 않습니다. entry point 지정

확인할 표를 구분해서 읽습니다

readelf -h firmware.elf
readelf -S firmware.elf
readelf -l firmware.elf
readelf -r firmware.elf
objdump -dr firmware.o
명령확인할 내용
readelf -hELF 종류·아키텍처·entry point를 봅니다.
readelf -Ssection의 이름·타입·크기·주소를 봅니다. SHT_NOBITS는 파일에 같은 크기의 0 바이트가 꼭 저장됐다는 뜻이 아닙니다.
readelf -lloader가 보는 segment 구성을 봅니다. section 표와 같지 않습니다.
readelf -r아직 relocation 항목이 있는지 봅니다. 정적 최종 이미지에서 없어졌다고 모든 symbol 참조가 원래부터 상수였던 것은 아닙니다.
objdump -dr명령 해석과 해당 위치의 relocation을 함께 봅니다. 데이터 section을 명령처럼 해석하지 않도록 주의합니다.

실제 빌드의 ABI·명령 집합·endianness에 맞는 binutils를 사용해야 합니다. symbol의 숫자, 해당 주소에 저장된 데이터, 그 주소에서 실행할 명령은 서로 다른 질문입니다. readelf 옵션