[임베디드 OS 개발 프로젝트] 4. 부팅하기

Sijin·2025년 5월 27일

부팅

  • 부팅이란?
    • 시스템에 전원이 들어가고 모든 초기화 작업을 마친 후 idle 상태에서 대기하기 까지
    • 시스템에 전원이 들어가고 arm 코어가 리셋 익셉션 핸들러를 모두 처리한 뒤 본격적인 C 코드로 진입하기 직전까지
  • 보통은 전자가 일반적이나, 우리는 후자를 부팅이라고 보자

메모리 설계

  • 실행파일은 메모리를 크게 3가지로 나눠 사용한다
영역용도
text 영역코드가 있는 공간
data 영역초기화한 전역 변수가 있는 공간
bss 영역초기화하지 않은 전역 변수가 있는 공간
빌드된 바이너리에는 symbol과 크기만 들어가있다
  • 임베디드 시스템에서 메모리는 어떻게 배치하는 것이 좋을까?

속도

  • 메모리는 속도와 크기가 반비례한다
    • 속도가 빠르고 크기가 작은 메모리에는 text 영역, 일부 속도에 민감한 변수들이 적합할 것이다
    • 속도가 느리고 크기가 큰 메모리에는 나머지 영역이 적합할 것이다
  • qemu는 그런 구분이 없으므로, 우선은 순서대로 배치하자

크기

  • 메모리의 크기는 얼마나 잡아야할까?
  • text 영역
    • RTOS를 사용하는 펌웨어는 많아야 수십 KB면 충분하다
    • 우리는 qemu 환경이고 예제이니, text영역에 1mb를 할당하자
    • 1mb는 0x00000000~0x000FFFFF의 범위이다
  • data 영역, bss 영역
    • 데이터의 성격을 생각해보자
      • 데이터의 형태: 동작 모드별 스택, 태스크 스택, 전역 변수, 동적 메모리 할당 영역이 있다
      • 데이터의 속성: 성능을 중시하는 데이터, 큰 공간이 필요한 데이터, 공유 데이터
    • 태스크 스택에 성능 속성을 부여해 배치하거나, 공간만 생각해 배치할 수 있다
    • 그러나 qemu에선 의미가 없으니 전역 변수 영역에 그냥 쭉 연속적으로 배치하자

설계

  • text 영역에 1mb를 할당했다
  • 각 동작 모드별로 1mb를 할당했다
    • usr모드와 sys모드는 메모리 공간과 레지스터를 공유하므로 하나로 묶어서 보았다
  • 태스크 스택 영역은 태스크마다 1mb씩 총 64개 태스크, 64mb를 할당했다
  • 전역 변수를 위해 1mb를 할당했다
  • 남는 공간은 동적 할당 메모리 영역으로 할당했다
영역주소
동적 할당 영역0x04900000 ~ 0x07FFFFFF (55mb)
전역 변수 영역0x04800000 ~ 0x048FFFFF (1mb)
태스크 스택 영역0x00800000 ~ 0x047FFFFF (64mb)
und 모드 영역0x00700000 ~ 0x007FFFFF (1mb)
abt 모드 영역0x00600000 ~ 0x006FFFFF (1mb)
fiq 모드 영역0x00500000 ~ 0x005FFFFF (1mb)
irq 모드 영역0x00400000 ~ 0x004FFFFF (1mb)
svc 모드 영역0x00300000 ~ 0x003FFFFF (1mb)
usr, sys 모드 영역0x00100000 ~ 0x002FFFFF (2mb)
text 영역0x00000000 ~ 0x000FFFFF (1mb)

[참고] 동작 모드

모드이름설명
usruser mode일반 사용자 프로그램이 실행되는 모드, 대부분 어플리케이션이 여기에 해당
syssystem mode시스템 모드, 커널 내부에서 사용되는 privileged 모드
svcsupervisor modeOS의 커널 코드가 실행되는 보호 모드, 시스템 콜 등 software interrupt 발생 시 진입
irqIRQ mode일반 interrupt 발생 시 진입하는 모드, hardware interrupt에 사용
fiqFIQ mode빠른 인터럽트 발생 시 진입하는 모드, 빠른 응답을 위해 별도 레지스터 뱅크를 가짐
abtabort mode메모리 접근 오류 발생 시 진입하는 모드, 예외 상황 처리에서 사용
undundefined mode정의되지 않은 명령 실행 시 진입, 잘못된 명령어 예외를 처리
  • arm에서는 여러 동작모드가 있다, 각 모드들은 특정 상황에서 cpu가 어떻게 동작할지 결정한다
  • 예외 모드마다 메모리 공간이 다른데, 각 예외 처리 모드가 자신만의 독립적인 스택 메모리 공간을 가지고 있다는 의미이다
    • usr모드와 sys모드는 메모리 공간, 레지스터를 공유한다
    • CPSR(Current Program Status Register)만 달라 해당 값으로 구분한다
  • 이를 통해 예외 처리 과정에서 기존 코드의 스택과 충돌하지 않고 독립적으로 예외를 처리할 수 있다

[참고] 뱅크드 레지스터

  • arm은 범용 레지스터 31개, 상태 레지스터 6개로 37개의 레지스터를 갖는다
  • 동작 모드별로 범용 레지스터 16개, 상태 레지스터 2개를 최대로 사용할 수 있다
  • 하지만 동작모드는 7가지이다, 모두 각각 레지스터를 따로 사용한다면 18 * 7 = 126개의 레지스터가 필요하다, 즉 arm에서는 동작 모드들이 레지스터를 공유해 사용한다
  • 레지스터는 아래와 같이 배정된다
    • r0 ~ r15, CPSR은 모든 동작모드에서 공유된다
    • r8_fiq ~ r14_fiq, SPSR_fiq는 FIQ 모드에서 사용된다
    • r13_svc, r14_svc, SPSR_svc는 SVC 모드에서 사용된다
    • ABT, UND, IRQ, MON 모드에서도 SVC모드와 같이 3개의 레지스터를 사용한다
  • FIQ 모드의 r8_fiq ~ r14_fiq까지의 레지스터는 FIQ 모드에서만 쓸 수 있다. 따라서, FIQ 모드에서 해당 레지스터들은 이전 레지터의 값을 백업할 필요가 없는데, fast interrupt라는 이름은 이런 동작방식 때문이다
  • 이렇게 각 동작모드마다 공유하지 않고 전용으로 사용되는 레지스터들을 뱅크드 레지스터라고 한다
  • 레지스터마다 용도는 아래와 같다
레지스터용도
R0 ~ R12범용 레지스터, 데이터를 일반적으로 처리하는데 사용
R13스택 포인터(SP), 스택을 사용하기 위한 stack의 top 위치
R14링크 레지스터(LR), 함수 분기 이후 return address를 저장
예를 들어, funcA에서 funcB를 호출한 상황에서 funcA의 호출 지점을 저장
R15프로그램 카운터(PC), 현재 명령어 메모리 주소
arm 모드에서는 4byte, thumb 모드에서는 2byte가 증가

익셉션 벡터 테이블 만들기

  • 익셉션 핸들러 동작을 위한 익셉션 벡터 테이블을 만들어보자
.text
    .code 32

    .global vector_start
    .global vector_end

    vector_start:
        LDR     PC, reset_handler_addr
        LDR     PC, undef_handler_addr
        LDR     PC, svc_handler_addr
        LDR     PC, pftch_abt_handler_addr
        LDR     PC, data_abt_handler_addr
        B       .
        LDR     PC, irq_handler_addr
        LDR     PC, fiq_handler_addr

        reset_handler_addr:     .word   reset_handler
        undef_handler_addr:     .word   dummy_handler
        svc_handler_addr:       .word   dummy_handler
        pftch_abt_handler_addr: .word   dummy_handler
        data_abt_handler_addr:  .word   dummy_handler
        irq_handler_addr:       .word   dummy_handler
        fiq_handler_addr:       .word   dummy_handler

    vector_end:

    reset_handler:
        LDR     R0, =0x10000000
        LDR     R1, [R0]
        
    dummy_handler:
        B       .
.end
  • 익셉션 벡터 테이블에 각 핸들러로 점프하는 코드만 작성했다
    • reset_handler에는 이전에 만들었던 ID Register 값을 읽어오는 핸들러이다
    • dummy_handler는 나중에 작성할 내용이니, 우선 무한루프를 돌도록 만들었다
  • 가장 먼저 리셋 익셉션 핸들러를 만들자
    • 리셋 익셉션 핸들러에서는 메모리 맵을 설정해주어야 한다
    • 동작 모드 별 스택 주소를 각 동작 모드의 뱅크드 레지스터 SP에 설정해주자
    • 7개 동작모드 중 USR 모드와 SYS 모드는 레지스터를 공유하므로, SP 레지스터는 총 6개의 뱅크드 레지스터로 제공된다
    • 리셋 익셉션 핸들러에서는 이 동작모드를 순서대로 변경해 가면서 SP 레지스터에 정해진 값을 넣을 것이다

[참고] 익셉션 핸들러

  • ARM 머신에서는 전원이 켜지면 익셉션 벡터 테이블의 리셋 벡터를 읽는다
  • 익셉션 벡터 테이블은 기본적으로 0x00000000에 위치한다
  • 익셉션은 총 7개가 있다
offset이름설명
0x00Reset전원이 켜지면 실행됨
0x04Undefined Instruction잘못된 명령어를 실행했을 때
0x08SVC(Supervisor Call)SVC 명령으로 발생시키는 익셉션
0x0CPrefetch Abort명령어 메모리에서 명령어를 읽다가 문제가 생김
0x10Data Abort데이터 메모리에서 데이터를 읽다가 문제가 생김
0x14Not used사용하지 않음
0x18IRQ interruptIRQ 인터럽트가 발생한 경우
0x1CFIQ interruptFIQ 인터럽트가 발생한 경우
  • ARM은 익셉션 벡터 테이블에 정의된 상황이 발생하면 PC(Program Counter)를 익셉션 벡터 테이블에 정의된 주소로 강제 변환된다
  • 익셉션은 각각 4byte = 32bit씩 할당되어있다. 즉, 32bit 머신이므로 한 익셉션에 한 명령어만 실행할 수 있다
    • 개발자 / 시스템 / 펌웨어마다 익셉션에 필요한 코드는 다를 것이다
    • 특정한 크기의 익셉션 코드 공간을 만들어두면, 낭비되는 공간이 생기는 경우가 있을 것이다
    • 따라서, 명령어를 쓸 수 있는 최소 공간(4byte)를 배정해두고, 그 익셉션을 처리하는 코드로 점프하게 만든다
    • 이때 익셉션을 처리하는 코드를 익셉션 핸들러라고 부른다

익셉션 종류

  • 익셉션이란, 주변장치 등에서 발생한 신호를 처리하기 위해 순차적으로 진행하던 프로그램의 수행을 끊는 것 이다
  • 익셉션이 발생하면 프로그램의 흐름과 상관 없이 무조건 익셉션 벡터를 통해 익셉션 핸들러로 진입한다
  • 이때, 기존 프로그램의 레지스터 유효성이 깨지므로 익셉션 처리 이후에 다시 돌아가기 위한 조치를 해주어야 한다
  • ARM은 익셉션 처리 후 원래 프로그램 흐름으로 복귀할 수 있도록 하드웨어 수준에서 최소한의 작업, R14(LR)에 복귀할 주소를 저장해준다.
  • 예를 들어, USR 모드에서 프로그램이 수행되다가 익셉션이 발생해서 IRQ 모드로 바뀌면, ARM은 자동으로 R14_irq에 다음에 실행할 명령어 위치인 PC+4를 저장한다
  • 다만 ARM 파이프라인 때문에 각 익셉션 별로 정상 복귀를 위해서 R14에 각각 다른 연산이 들어갈 수 있다
    • ARM 파이프라인은 컴퓨터 구조론에서 이야기하는 fetch-decode-execute를 의미한다
    • 16bit instruction set을 사용하는 thumb 모드 / 32bit instructino set을 사용하는 arm 모드
익셉션복귀 주소동작모드 arm동작모드 thumb
SVC (Supervisor call)MOVS PC, R14 svcPC+4PC+2
SMC (Secure Monitor Call)MOVS PC, R14 monPC+4-
UNDEF (Undefined Instruction)MOVS PC, R14 undPC+4PC+2
PABT (Prefetch Abort)MOVS PC, R14 #4PC+4PC+2
FIQ (Fast Interrupt Request)SUBS PC, R14 q, #4PC+4PC+4
IR (Interrupt Request)QSUBS PC, R14 irq, #4PC+4PC+4
DABT (Data Abort)SUBS PC, R14 abt, #8PC+8PC+8
RESET---
BKPT (Breakpoint)SUBS PC, R14 abt, #4PC+4PC+4

스택 만들기

  • c언어 헤더 파일도 gcc로 컴파일하면 Entry.S 어셈블리 파일에서 사용할 수 있다
  • 우선 각 동작 모드 별 스택 시작 주소, 스택 크기, 스택 마지막 주소를 정의해보자
# MemoryMap.h

#define INST_ADDR_START         0x0             // text 영역
#define USRSYS_STACK_START      0x00100000      // usr, sys 모드 영역
#define SVC_STACK_START         0x00300000      // svc 모드 영역
#define IRQ_STACK_START         0x00400000      // irq 모드 영역
#define FIQ_STACK_START         0x00500000      // fiq 모드 영역
#define ABT_STACK_START         0x00600000      // abt 모드 영역
#define UND_STACK_START         0x00700000      // und 모드 영역
#define TASK_STACK_START        0x00800000      // 태스크 스택 영역
#define GLOBAL_ADDR_START       0x04800000      // 전역 변수 영역
#define DALLOC_ADDR_START       0x04900000      // 동적 할당 영역

#define INST_MEM_SIZE           (USRSYS_STACK_START - INST_ADDR_START)
#define USRSYS_STACK_SIZE       (SVC_STACK_START - USRSYS_STACK_START)
#define SVC_STACK_SIZE          (IRQ_STACK_START - SVC_STACK_START)
#define IRQ_STACK_SIZE          (FIQ_STACK_START - IRQ_STACK_START)
#define FIQ_STACK_SIZE          (ABT_STACK_START - FIQ_STACK_START)
#define ABT_STACK_SIZE          (UND_STACK_START - ABT_STACK_START)
#define UND_STACK_SIZE          (TASK_STACK_START - UND_STACK_START)
#define TASK_STACK_SIZE         (GLOBAL_ADDR_START - TASK_STACK_START)
#define DALLOC_STACK_SIZE       (55 * 1024 * 1024)

#define USRSYS_STACK_TOP        (USRSYS_STACK_START + USRSYS_STACK_SIZE - 4)
#define SVC_STACK_TOP           (SVC_STACK_START + SVC_STACK_SIZE - 4)
#define IRQ_STACK_TOP           (IRQ_STACK_START + IRQ_STACK_SIZE - 4)
#define FIQ_STACK_TOP           (FIQ_STACK_START + FIQ_STACK_SIZE - 4)
#define ABT_STACK_TOP           (ABT_STACK_START + ABT_STACK_SIZE - 4)
#define UND_STACK_TOP           (UND_STACK_START + UND_STACK_SIZE - 4)
  • 다음으로, CPSR에 값을 설정해 동작모드를 바꿀 수 있는 값을 정의해보자
# ARMv7AR.h

#define ARM_MODE_BIT_USR    0x10
#define ARM_MODE_BIT_FIQ    0x11
#define ARM_MODE_BIT_IRQ    0x12
#define ARM_MODE_BIT_SVC    0x13
#define ARM_MODE_BIT_ABT    0x17
#define ARM_MODE_BIT_UND    0x1B
#define ARM_MODE_BIT_SYS    0x1F
#define ARM_MODE_BIT_MON    0x16
  • 정의한 헤더들을 리셋 익셉션 핸들러에 포함시켜보자
#include "MemoryMap.h"
#include "ARMv7AR.h"

.text
    .code 32

    .global vector_start
    .global vector_end

    vector_start:
        LDR     PC, reset_handler_addr
        LDR     PC, undef_handler_addr
        LDR     PC, svc_handler_addr
        LDR     PC, pftch_abt_handler_addr
        LDR     PC, data_abt_handler_addr
        B       .
        LDR     PC, irq_handler_addr
        LDR     PC, fiq_handler_addr

        reset_handler_addr:     .word   reset_handler
        undef_handler_addr:     .word   dummy_handler
        svc_handler_addr:       .word   dummy_handler
        pftch_abt_handler_addr: .word   dummy_handler
        data_abt_handler_addr:  .word   dummy_handler
        irq_handler_addr:       .word   dummy_handler
        fiq_handler_addr:       .word   dummy_handler

    vector_end:

    reset_handler:
        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_SVC
        MSR cpsr, r1
        LDR sp, =SVC_STACK_TOP

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_IRQ
        MSR cpsr, r1
        LDR sp, =IRQ_STACK_TOP

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_FIQ
        MSR cpsr, r1
        LDR sp, =FIQ_STACK_TOP

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_ABT
        MSR cpsr, r1
        LDR sp, =ABT_STACK_TOP

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_UND
        MSR cpsr, r1
        LDR sp, =UND_STACK_TOP

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_SYS
        MSR cpsr, r1
        LDR sp, =USRSYS_STACK_TOP
        
    dummy_handler:
        B       .
.end
  • reset_handler 동작 후 별도 작업 없이 RTOS의 기본 동작 모드인 SYS모드로 작업을 이어나가기 위해 SYS모드를 마지막으로 설정하였다
  • 실무에서는 시스템 클럭 설정, 메모리 컨트롤러 초기화 등 추가 작업이 필요하다
  • reset_handler는 아래 코드를 반복한다
        @ cpsr의 값을 r0로 읽어옴
        MRS r0, cpsr
        
        @ r0에서 하위 5bit(0x1f)를 NOT AND해서 r1에 저장
        @ 즉 하위 5bit는 0이 됨, 하위 5bit는 동작 모드를 의미
        BIC r1, r0, #0x1F
        
        @ r1에 동작 모드를 입력
        ORR r1, r1, #{동작 모드}
        
        @ r1의 값을 cpsr을 입력
        MSR cpsr, r1
        
        @ 해당 모드에서 사용할 스택 포인터를 뱅크드 레지스터인 sp에 입력
        LDR sp, ={동작모드 별 stack top 주소}
  • 빌드를 위해 Makefile을 수정하자
    • 어셈블리 코드에 추가된 #include 같은 전처리기는 arm-none-eabi-gcc를 사용해야 하므로 컴파일러를 바꿔준다
    • 어셈블리 코드에 include한 헤더파일이 포함될 수 있도록 INC_DIRS를 -I 옵션으로 gcc에 추가해야 한다
    • gcc는 기본적으로 실행파일을 만들지만 우리는 오브젝트 파일을 만들어야 하므로, -c 옵션을 추가해야 한다
# Makefile

...
ASM_SRCS = $(wildcard boot/*.S)
ASM_OBJS = $(patsubst boot/%.S, build/%.o, $(ASM_SRCS))

INC_DIRS = include

navilos= build/navilos.axf
navilos_bin = build.navilos.navilos_bin

...

build/%.o: boot/%.S
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
  • 만들어진 스택을 확인해보자
vector_end () at boot/Entry.S:37
37              MRS r0, cpsr
(gdb) s
38              BIC r1, r0, #0x1F
(gdb) s
39              ORR r1, r1, #ARM_MODE_BIT_IRQ
(gdb) s
40              MSR cpsr, r1
(gdb) s
vector_end () at boot/Entry.S:41
41              LDR sp, =IRQ_STACK_TOP
(gdb) s
vector_end () at boot/Entry.S:43
43              MRS r0, cpsr
(gdb) info register
r0             0x400001d3          1073742291
r1             0x400001d2          1073742290
r2             0x0                 0
r3             0x0                 0
r4             0x0                 0
r5             0x0                 0
r6             0x0                 0
r7             0x0                 0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x0                 0
sp             0x4ffffc            0x4ffffc
lr             0x0                 0
pc             0x64                0x64 <vector_end+40>
cpsr           0x400001d2          1073742290
  • IRQ 모드의 스택을 확인해보자
    • sp는 0x4ffffc이므로, IRQ_STACK_TOP 값으로 설정되었다
    • cpsr이 0x400001d2, 하위 5bit는 0x12이므로 ARM_MODE_BIT_IRQ 값으로 잘 설정되었다

main함수로 진입

        MRS r0, cpsr
        BIC r1, r0, #0x1F
        ORR r1, r1, #ARM_MODE_BIT_SYS
        MSR cpsr, r1
        LDR sp, =USRSYS_STACK_TOP

        BL main
  • reset_handler 마지막에 main함수로 진입하는 코드를 추가하자
    • BL main
  • main함수는 boot/Main.c에 정의하자
# Main.c

#include "stdint.h"

void main(void)
{
    uint32_t* dummyAddr = (uint32_t*)(1024 * 1024 * 100);
    *dummyAddr = sizeof(long);
}
  • main함수는 아직 의미있는 동작을 하지는 않는다
    • 100mb 메모리 주소 0x64000000에 4(sizeof long)을 쓴다
  • 어셈블리에서 브랜치 명령인 BL로 점프하기 위해서는 점프하려는 레이블이 .global에 선언되어있어야 한다
  • 컴파일러는 c언어 함수 이름을 링커가 자동으로 접근할 수 있도록 전역 심벌로 만든다
    • 어셈블리어를 쓸 때 .global 지시어로 선언하면 전역 심벌이 된다
    • c언어를 쓸 때, extern으로 선언하면 전역 심벌이 된다
    • 반대로 어셈블리에서 .global로 선언한 이름은 c언어에서 사용할 수 있다
  • stdint는 아래에서 다운받아서 include 안에 넣어주었다
  • Makefile을 수정해 c언어 소스 파일을 컴파일할 수 있도록 하자
ARCH=armv7-a
MCPU=cortex-a8

CC=arm-none-eabi-gcc
AS=arm-none-eabi-as
LD=arm-none-eabi-ld
OC=arm-none-eabi-objcopy

LINKER_SCRIPT = ./navilos.ld
MAP_FILE = build/navilos.map

ASM_SRCS = $(wildcard boot/*.S)
ASM_OBJS = $(patsubst boot/%.S, build/%.os, $(ASM_SRCS))

C_SRCS = $(wildcard boot/*.c)
C_OBJS = $(patsubst boot/%.c, build/%.o, $(C_SRCS))

INC_DIRS = include

navilos= build/navilos.axf
navilos_bin = build.navilos.navilos_bin

.PHONY: all clean run debug gdb

all: $(navilos)

clean:
	@rm -rf build

run: $(navilos)
	qemu-system-arm -M realview-pb-a8 -kernel $(navilos)

debug: $(navilos)
	qemu-system-arm -M realview-pb-a8 -kernel $(navilos) -S -gdb tcp::1234,ipv4

gdb:
	gdb-multiarch

$(navilos): $(ASM_OBJS) $(C_OBJS) $(LINKER_SCRIPT)
	$(LD) -n -T $(LINKER_SCRIPT) -o $(navilos) $(ASM_OBJS) $(C_OBJS) -Map=$(MAP_FILE)
	$(OC) -O binary $(navilos) $(navilos_bin)

build/%.os: $(ASM_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<

build/%.o: $(C_SRCS)
	mkdir -p $(shell dirname $@)
	$(CC) -march=$(ARCH) -mcpu=$(MCPU) -I $(INC_DIRS) -c -g -o $@ $<
  • Makefile에서 아래 내용들을 수정하였다
    • 어셈블리 오브젝트 파일들은 .os로 정의
    • c 오브젝트 파일들은 .o로 정의
    • map 파일 정의
      • map파일은 링커가 생성하는 파일로, 링킹 작업 때 심벌에 할당된 메모리 주소를 기록
    • C_SRCS, C_OBJS 변수를 만들고 build 명령어 추가
    • navilos 생성 시 C_OBJS를 추가하고 map 파일 설정

main 함수 동작 확인

(gdb) 
(gdb) target remote:1234
Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x000000b8 in ?? ()
(gdb) file build/navilos.axf 
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from build/navilos.axf...
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
dummy_handler () at boot/Entry.S:70
70              B       .
(gdb) x/8wx 0x06400000
0x6400000:      0x00000004      0x00000000      0x00000000      0x00000000
0x6400010:      0x00000000      0x00000000      0x00000000      0x00000000
(gdb) 
  • gdb에서 c명령어로 qemu 펌웨어를 실행하자
    • 우리가 지금까지 만든 펌웨어는 우선 reset_handler가 실행되고
    • reset_handler에 이어 main함수가 호출된 후
    • main 함수 종료 후 dummy_handler가 호출되어 무한루프를 돌게 될 것이다
    • c명령을 입력하면 무한루프로 인해 종료되지 않으므르 ctrl+c로 강제종료하자
  • main 함수가 잘 동작했다면 0x06400000에 4가 들어가있을것이다
    • x/8wx 명령은 입력된 메모리부터 8개를 4byte씩 16진수로 출력한다
    • 0x06400000에 예상대로 4가 들어가있음을 확인할 수 있다

0개의 댓글